传送门biu~
对棋盘进行黑白染色。设黑格个数为num0,数值和为sum0;白格个数为num1 数值和为sum1。
最后都变为x,则num0 * x – sum0 = num1 * x – sum1,所以x = (sum0 – sum1) / (num0 – num1)。当num0 ≠ num1时,可以解出 x 再用网络流验证;当num1 = num2时,可以发现对于一个合法的x k>=x都是一个合法的解。所以可以二分x,再用网络流验证。
建图时,如果点k为白,建边(s, k, x – v[k]);如果为黑,建边(k, t, x – v[k]);相邻点u、v (u为白)
建边 (u, v, INF)。
#include<bits/stdc++.h>
using namespace std;
const long long INF=1LL<<50;
long long sum0,sum1;
int num0,num1;
int n,m,cnt,S,T;
int xx[4]={0,0,1,-1},yy[4]={1,-1,0,0};
int a[45][45],color[45][45];
int head[2005],fir[2005],dep[2005],nex[20005],to[20005],tp;
long long cap[20005];
inline void add(int x,int y,long long c){
nex[++tp]=head[x];
head[x]=tp;
to[tp]=y;
cap[tp]=c;
}
inline void Insert(int u,int v,long long w){add(u,v,w);add(v,u,0);}
inline int bfs(){
memset(dep,0,sizeof(dep));
dep[S]=1;queue<int>q;
q.push(S);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=nex[i]){
if(cap[i] && !dep[to[i]]){
dep[to[i]]=dep[x]+1;
q.push(to[i]);
}
}
}
return dep[T];
}
long long dfs(int x,long long now){
if(x==T || !now) return now;
long long c=0;
for(int &i=fir[x];i;i=nex[i]){
if(dep[to[i]]==dep[x]+1 && cap[i]){
long long f=dfs(to[i],min(now,cap[i]));
now-=f;
cap[i]-=f;
cap[i^1]+=f;
c+=f;
if(!now) break;
}
}
return c;
}
inline long long Dinic(){
long long c=0;
while(bfs()){
for(int i=S;i<=T;++i) fir[i]=head[i];
c+=dfs(S,INF);
}
return c;
}
inline bool check(long long x){
memset(head,0,sizeof(head));tp=1;
S=0;T=n*m+1;
long long tot=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(color[i][j]){
Insert(S,(i-1)*m+j,x-a[i][j]);tot+=x-a[i][j];
for(int k=0;k<4;k++){
int nowx=i+xx[k],nowy=j+yy[k];
if(nowx<1||nowy<1||nowx>n||nowy>m)continue;
Insert((i-1)*m+j,(nowx-1)*m+nowy,INF);
}
}
else Insert((i-1)*m+j,T,x-a[i][j]);
return (Dinic()==tot);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
num0=num1=sum0=sum1=0;
scanf("%d%d",&n,&m);
int Max=0;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
scanf("%d",&a[i][j]);
color[i][j]=(i+j)&1;
Max=max(Max,a[i][j]);
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(color[i][j])sum1+=a[i][j],++num1;
else sum0+=a[i][j],++num0;
}
}
if(num0!=num1){
long long x=(sum0-sum1)/(num0-num1);
if(x>=Max){
if(check(x)){
printf("%lld\n",x*num1-sum1);
continue;
}
}
printf("-1\n");
}
else{
if(sum0!=sum1){
printf("-1\n");
continue;
}
long long l=Max,r=INF;
while(l<=r){
long long mid=l+r>>1;
if(check(mid)) r=mid-1;
else l=mid+1;
}
printf("%lld\n",l*num1-sum1);
}
}
return 0;
}