【网络流】【1010】【棋盘加数】
题目大意
一个 N*M 的棋盘上每个格子有一个数。每次选择两个相邻的格子,并使这两个数都加上 1。问最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1。
- 显然有黑白染色的特征
- 显然是二分图
- 黑色和白色的数值差永远不变
- 如果知道变成什么数,可以用网络流来判断是否可以
做法
- 如果N或M为偶数可以二分+网络流解决
- 如果N和M都为奇数可以利用以下等式
(黑色格子数-白色格子数)*最终情况格子的值=黑色初始格子总值-白色初始格子总值
得到最终情况格子的值,所以为奇数的时候是一个确定的值
坑点
- 注意MAXN,MAXM的确定,小了的话,会超时,很难发现是因为这个超时的!!!
- long long
代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#define oo 0x13131313
using namespace std;
const int MAXN=2000+5;
const int MAXM=10000;
long long INF=1LL<<50;
int ok=1;
int fx[5]={0,0,0,1,-1},fy[6]={0,1,-1,0,0};
struct Edge
{
long long to,next,cap,flow;
void get(long long a,long long b,long long c,long long d)
{
to=a;next=b;cap=c;flow=d;
}
}edge[MAXM];
long long tol;
long long head[MAXN];
long long gap[MAXN],dep[MAXN],pre[MAXN],cur[MAXN];
void init()
{
tol=0;
memset(head,-1,sizeof(head));
}
//¦Ì£¤Ï¨°¨ª¼¨¨y¸ö2Ψºy¡ê¬ÎTϨ°¨ª¼Ëĸö2Ψºy
void addedge(long long u,long long v,long long w,long long rw=0)
{
edge[tol].get(v,head[u],w,0);head[u]=tol++;
edge[tol].get(u,head[v],rw,0);head[v]=tol++;
}
long long sap(long long start,long long end,long long N)
{
memset(gap,0,sizeof(gap));
memset(dep,0,sizeof(dep));
memcpy(cur,head,sizeof(head));
long long u=start;
pre[u]=-1;
gap[0]=N;
long long ans=0;
while(dep[start]<N)
{
if(u==end)
{
long long Min=INF;
for(long long i=pre[u];i!=-1;i=pre[edge[i^1].to])
if(Min>edge[i].cap-edge[i].flow)
Min=edge[i].cap-edge[i].flow;
for(long long i=pre[u];i!=-1;i=pre[edge[i^1].to])
{
edge[i].flow+=Min;
edge[i^1].flow-=Min;
}
u = start;
ans+=Min;
continue;
}
bool flag=false;
long long v;
for(long long i=cur[u];i !=-1;i=edge[i].next)
{
v=edge[i].to;
if(edge[i].cap-edge[i].flow&&dep[v]+1==dep[u])
{
flag=true;
cur[u]=pre[v]=i;
break;
}
}
if(flag)
{
u=v;
continue;
}
long long Min=N;
for(long long i=head[u];i!=-1;i=edge[i].next)
if(edge[i].cap-edge[i].flow&&dep[edge[i].to]<Min)
{
Min=dep[edge[i].to];
cur[u]=i;
}
gap[dep[u]]--;
if(!gap[dep[u]]) return ans;
dep[u]=Min+1;
gap[dep[u]]++;
if(u!=start) u=edge[pre[u]^1].to;
}
return ans;
}
long long N,M;
long long s,t,NN;
long long MAP[100][100];
long long b=0,w=0;
long long sum=0;
long long nb,nw;
void input()
{
nb=0,nw=0;
b=0,w=0;sum=0;
cin>>N>>M;
for(long long i=1;i<=N;i++)
for(long long j=1;j<=M;j++)
{
scanf("%I64d",&MAP[i][j]);
sum+=MAP[i][j];
if((i+j)%2==0)
b=b+MAP[i][j],nb++;
else
w=w+MAP[i][j],nw++;
}
}
void creatgraph(long long c)
{
init();
s=0;t=N*M+1;
//s-> && ->oo
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
{
if((i+j)%2==0)
{
if(c-MAP[i][j]>=0)
addedge(s,(i-1)*M+j,c-MAP[i][j]);
else ok=0;
for(int k=1;k<=4;k++)
{
long long xx=i+fx[k],yy=j+fy[k];
if(1<=xx&&xx<=N&&1<=yy&&yy<=M)
{
//debug
// printf("%d %d->%d %d\n",i,j,xx,yy);
addedge((i-1)*M+j,(xx-1)*M+yy,INF);
}
}
}
}
//->t
for(long long i=1;i<=N;i++)
for(long long j=1;j<=M;j++)
{
if((i+j)%2==1)
if(c-MAP[i][j]>=0)
addedge((i-1)*M+j,t,c-MAP[i][j]);
else ok=0;
}
}
void solve1()
{
if(w!=b) {
printf("-1\n");
return ;
}
else {
long long l=0,r=1LL<<40;
while(l<r)
{
long long m=(l+r)/2;
ok=1;
creatgraph(m);
long long a=sap(s,t,N*M+2);
long long b=(N*M*m-sum)/2;
// printf("%I64d %I64d\n",a,b);
if(a==b&&ok==1) r=m;
else l=m+1;
}
printf("%I64d\n",(N*M*l-sum)/2);
}
}
void solve2()
{
if(b>w)
{
long long m=b-w;
creatgraph(m);
if(sap(s,t,N*M+2)==(N*M*m-sum)/2)
printf("%I64d\n",(N*M*m-sum)/2);
else printf("-1\n");
}
else printf("-1\n");
}
int main()
{
// freopen("a.in","r",stdin);
long long T;
cin>>T;
while(T--)
{
ok=1;
input();
if(N%2==0||M%2==0)
solve1();
else solve2();
}
}