Description
Blinker最近喜欢上一个奇怪的游戏。
这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数。每次 Blinker 会选择两个相邻
的格子,并使这两个数都加上 1。
现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同
一个数则输出-1。
Input
输入的第一行是一个整数T,表示输入数据有T轮游戏组成。
每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。
接下来有N行,每行 M个数。
Output
对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。
Sample Input
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
Sample Output
-1
HINT
【数据范围】
对于30%的数据,保证 T<=10,1<=N,M<=8
对于100%的数据,保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000
题解:
首先对图黑白染色.
设白色的数量为n1,权值和为s1.黑色的数量为n2,权值和为s2.
设最后变成的数是x.则n1*x-s1=n2*x-s2;
这样如果n1!=n2我们可以直接把x解出来.
如果n1==n2,可以发现x合法那么大于x的数都合法.
我们就可以二分x.
现在问题变成了如何判断一个x合法.这个可以用最大流来处理.
源点向白点连容量为(x-a[i][j])的边.
黑点向汇点连容量为(x-a[i][j])的边.
白点向黑点连容量为inf的边.
判断最大流是否等于n1*x-s1即可.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 2000
#define M 20000
#define inf 1LL<<50
using namespace std;
int T,t,c[N][N],n,m,a[50][50],xi[4]={1,0,-1,0},yi[4]={0,1,0,-1};
int point[N],next[M<<1],cur[N],gap[N],dis[N],pre[N],cnt;
long long sum1,sum2,num1,num2,x,mx;
struct use{
int st,en;
long long v;
}e[M<<1];
void add(int x,int y,long long v){
next[++cnt]=point[x];point[x]=cnt;
e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;
next[++cnt]=point[y];point[y]=cnt;
e[cnt].st=y;e[cnt].en=x;e[cnt].v=0;
}
long long isap(){
int i,u(1);long long ans(0);
memset(gap,0,sizeof(gap));gap[0]=T;
memset(dis,0,sizeof(dis));
for (i=1;i<=T;i++) cur[i]=point[i];
while (dis[1]<T){
int f(0);
for (i=cur[u];i;i=next[i])
if (e[i].v&&dis[e[i].en]+1==dis[u]){f=1;cur[u]=i;break;}
if (f){
u=e[i].en;pre[u]=i;
if (u==T){
long long mn=inf;
for (int i=T;i!=1;i=e[pre[i]].st) mn=min(mn,e[pre[i]].v);
ans+=mn;
for (int i=T;i!=1;i=e[pre[i]].st) e[pre[i]].v-=mn,e[pre[i]^1].v+=mn;
u=1;
}
}
else{
--gap[dis[u]];if (!gap[dis[u]]) return ans;
int mn=T;for (int i=point[u];i;i=next[i]) if (e[i].v) mn=min(mn,dis[e[i].en]);
gap[dis[u]=mn+1]++;cur[u]=point[u];if (u!=1) u=e[pre[u]].st;
}
}
return ans;
}
int cal(int x,int y){
return (x-1)*m+y;
}
bool check(long long x){
long long sum(0);
memset(point,0,sizeof(point));cnt=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (c[i][j]){
add(1,cal(i,j)+1,x-a[i][j]);sum+=x-a[i][j];
for (int k=0;k<4;k++){
int xx=i+xi[k],yy=j+yi[k];
if (xx<1||xx>n||yy<1||yy>m) continue;
add(cal(i,j)+1,cal(xx,yy)+1,inf);
}
}
else add(cal(i,j)+1,T,x-a[i][j]);
if (isap()==sum) return true;
else return false;
}
int main(){
scanf("%d",&t);
while (t--){
scanf("%d%d",&n,&m);T=n*m+2;
num1=num2=sum1=sum2=0;mx=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
c[i][j]=(i+j)%2;mx=max(mx,(long long)a[i][j]);
if (c[i][j]==1) num1++,sum1+=a[i][j];
else num2++,sum2+=a[i][j];
}
if (num1!=num2){
x=(sum1-sum2)/(num1-num2);
if (check(x))
printf("%lld\n",num1*x-sum1);
else printf("-1\n");
}
else{
long long l=mx,r=inf;
while (l<=r){
long long mid=(l+r)>>1;
if (check(mid)) r=mid-1;
else l=mid+1;
}
printf("%lld\n",num1*l-sum1);
}
}
}