题目链接
题目解法
很牛的题!!!
先考虑一步很牛的转化:把矩阵黑白染色,且 \(i+j\) 为奇数的位置的值取反,每次操作变为左上右下 \(+v\),左下右上 \(-v\)
这样有啥好处?操作不会使行和列的和改变
考虑答案的下界显然是:\(\max\{\)行的绝对值之和,列的绝对值之和\(\}\)
这里给出结论:答案就是这个下界
证明的话分两步:
第一步
给出一个结论:一个矩阵,可以通过一些操作,得到任何一个 行的和与原矩阵相同,且列的和与原矩阵相同 的矩阵
为啥?
我们将原矩阵和目标矩阵对应位相减,那么目的就是使这个新矩阵全为 \(0\)
一个显然的事情是我们可以将原矩阵变为 \((n-1)\times (m-1)\) 的 \(0\),最后一行和最后一列是新矩阵的对应行和列的和,由此可得整个矩阵都为 \(0\)
第二步
有了上面的结论,我们现在只需要构造一个行与列的和都固定,且答案为下界的一个矩阵即可
如何构造(令 \(row_i\) 为行 \(i\) 的和,\(col_j\) 为列 \(j\) 的和):
- 如果有同号的 \(row_x,col_y\),最优的方案是把绝对值小的消去,所以 \(b_{x,y}\Leftarrow b_{x,y}+\min(row_x,row_y)\)
- 在情况 1 不存在时,此时行和列中必有一个和都为 \(0\)
找到异号的 \(row_x,row_y\)(列同理),最优的方案是把绝对值小的消去
因为每次必能将一行或一列消成 \(0\),所以时间复杂度为 \(O(n^2)\)
#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
const int N=510;
int n,m,a[N][N];
LL row[N],col[N],b[N][N];
void doit(int x,int y,LL v){ b[x][y]+=v,row[x]-=v,col[y]-=v;}
int main(){
n=read(),m=read();
F(i,1,n) F(j,1,m){
a[i][j]=read();
if((i+j)&1) a[i][j]*=-1;
row[i]+=a[i][j],col[j]+=a[i][j];
}
while(true){
int rneg=0,rpos=0,cneg=0,cpos=0;
F(i,1,n){
if(row[i]<0) rneg=i;
if(row[i]>0) rpos=i;
}
F(i,1,m){
if(col[i]<0) cneg=i;
if(col[i]>0) cpos=i;
}
if(rneg&&cneg) doit(rneg,cneg,max(row[rneg],col[cneg]));
else if(rpos&&cpos) doit(rpos,cpos,min(row[rpos],col[cpos]));
else if(rneg&&rpos){
LL v=min(-row[rneg],row[rpos]);
doit(rneg,1,-v),doit(rpos,1,v);
}
else if(cneg&&cpos){
LL v=min(-col[cneg],col[cpos]);
doit(1,cneg,-v),doit(1,cpos,v);
}
else break;
}
LL ans=0;
F(i,1,n) F(j,1,m){
ans+=abs(b[i][j]);
if((i+j)&1) b[i][j]*=-1;
}
printf("%lld\n",ans);
F(i,1,n){ F(j,1,m) printf("%lld ",b[i][j]);puts("");}
return 0;
}
标签:rpos,Square,rneg,题解,矩阵,Add,cpos,col,row
From: https://www.cnblogs.com/Farmer-djx/p/18008833