网络流。
确实是网络流题目做太少,完全没往这方面去想。。。
对棋盘进行黑白相间染色,如果记s0表示黑格子数字和,s1表示白格子数字和,t1表示黑格子数量,t2表示白格子数量,操作之后数字全都是x,那么因为黑白格子的操作次数肯定一样,有
x∗t1−s1=x∗t0−s0
可以得到
x=s1−s0t1−t0
如果
t1≠t2
那么x是唯一的,直接判断即可
否则假设x可行,那么任何大于x的数一定可行,于是二分即可
至于怎么判断,网络流!
源点->所有黑格 容量是格子的数字
黑格->相邻白格 容量无限大
所有白格->汇点 容量是格子的数字
判断最大流是否等于x与所有数字差的和即可
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define N 42
#define INF (1LL<<50)
using namespace std;
struct edge{int next,to;long long v;}e[N*N*4*2];
long long s[2], a[N][N];
int S, T, last[N*N], p_cnt, t[2];
int n, m, cnt, pack[N][N], dx[4]={0,0,1,-1}, dy[4]={1,-1,0,0}, level[N*N];
bool color[N][N];
void add(int a, int b, long long w)
{
e[++cnt]=(edge){last[a],b,w};
last[a]=cnt;
e[++cnt]=(edge){last[b],a,0};
last[b]=cnt;
}
bool bfs()
{
queue<int> q;
memset(level,-1,sizeof(level));
q.push(S);
level[S]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(e[i].v && level[y]==-1)
{
level[y]=level[x]+1;
q.push(y);
}
}
}
return level[T]!=-1;
}
long long dfs(int x, long long f)
{
long long re=0;
if(x==T)return f;
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(level[x]+1!=level[y] || !e[i].v)continue;
long long use=dfs(y,min(f-re,e[i].v));
e[i].v-=use;
e[i^1].v+=use;
re+=use;
if(re==f)return f;
}
if(!re)level[x]=-1;
return re;
}
long long dinic()
{
long long re=0;
while(bfs())
re+=dfs(S,INF);
return re;
}
bool check(long long x)
{
memset(last,0,sizeof(last));
cnt=1;
S=0, T=n*m+2;
long long pre=0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
if(color[i][j])
{
pre+=x-a[i][j];
add(S,pack[i][j],x-a[i][j]);
for(int k = 0; k < 4; k++)
{
int nx=i+dx[k], ny=j+dy[k];
if(nx<1||ny<1||nx>n||ny>m)continue;
add(pack[i][j],pack[nx][ny],INF);
}
}
else
add(pack[i][j],T,x-a[i][j]);
}
return dinic()==pre;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
p_cnt=0;
memset(s,0,sizeof(s));
memset(t,0,sizeof(t));
long long mx=0;
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
scanf("%lld",&a[i][j]);
mx=max(mx,a[i][j]);
pack[i][j]=++p_cnt;
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
color[i][j]=(i+j)&1;
s[color[i][j]]+=a[i][j];
t[color[i][j]]++;
}
if(t[0]!=t[1])
{
long long x=(s[0]-s[1])/(t[0]-t[1]);
if(x<mx)printf("-1\n");
else printf("%lld\n",check(x)?(x*t[0]-s[0]):-1);
}
else
{
if(s[0]!=s[1])printf("-1\n");
else
{
long long l = mx, r = INF;
while(l<r)
{
long long mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%lld\n",l*t[1]-s[1]);
}
}
}
}