奇怪的游戏
题目描述
Blinker最近喜欢上一个奇怪的游戏。
这个游戏在一个N*M的棋盘上玩,每个格子有一个数。每次Blinker会选择两个相邻的格子,并使这两个数都加上1。
现在Blinker想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1。
输入
输入的第一行是一个整数T,表示输入数据有T轮游戏组成。
每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。
接下来有N行,每行M个数。
输出
对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。
样例输入
2
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
样例输出
2
-1
提示
【数据范围】
对于30%的数据,保证 T<=10,1<=N,M<=8
对于100%的数据,保证 T<=10,1<=N,M<=40,所有数为正整数且小于1000000000
solution
对棋盘进行黑白染色
设黑格个数为num1 数值和为sum1
设白格个数为num1 数值和为sum1
设最后都变为x
则
num1 * x – sum1 = num2 * x – sum2
x = (sum1 – sum2) / (num1 – num2)
当num1 ≠ num2时 可以解出 x 再用网络流check即可
对于num1 = num2时 可以发现 对于一个合法的x k>=x都是一个合法的解
因为num1 = num2 => (num1 + num2) % 2 == 0 可以构造一层的满覆盖
所以可以二分x 然后用网络流check
建图:
如果点k为白
建边(s, k, x – v[k])
如果为黑
建边(k, t, x – v[k])
对相邻点u、v (u为白)
建边 (u, v, inf)
黄学长写的真好orz
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define inf 1e15
#define maxn 10005
using namespace std;
int Te,n,m,num1,num2;
int head[maxn],cur[maxn],S,T,tot,flag[maxn];
long long s[45][45],sum1,sum2,Max,l,r,d[maxn];
struct node{
int v,nex;
long long cap;
}e[200005];
int get(int i,int j){
return (i-1)*m+j;
}
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
bool can(int i,int j){
if(i>0&&i<=n&&j>0&&j<=m)return 1;
return 0;
}
void lj(int t1,int t2,long long t3){
e[++tot].v=t2;e[tot].cap=t3;e[tot].nex=head[t1];head[t1]=tot;
e[++tot].v=t1;e[tot].cap=0;e[tot].nex=head[t2];head[t2]=tot;
}
bool BFS(){
for(int i=1;i<=T;i++)d[i]=inf;
queue<int>q;q.push(S);d[S]=0;
while(!q.empty()){
int x=q.front();q.pop();cur[x]=head[x];
for(int i=head[x];i;i=e[i].nex){
if(d[e[i].v]>d[x]+1&&e[i].cap){
d[e[i].v]=d[x]+1;
if(!flag[e[i].v]){
q.push(e[i].v);flag[e[i].v]=1;
}
}
}
flag[x]=0;
}
return d[T]!=inf;
}
long long lian(int k,long long a){
if(k==T||!a)return a;
long long f,flow=0;
for(int &i=cur[k];i;i=e[i].nex){
if(d[e[i].v]==d[k]+1&&(f=lian(e[i].v,min(e[i].cap,a)))>0){
flow+=f;a-=f;
e[i].cap-=f;e[i^1].cap+=f;
if(!a)break;
}
}
return flow;
}
bool pd(long long mid){
S=10000,T=10001;tot=1;
long long cnt=0,ans=0;
for(int i=1;i<=T;i++)head[i]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int now=get(i,j);
if(mid<s[i][j])return 0;
if((i+j)&1){
lj(S,now,mid-s[i][j]);cnt+=mid-s[i][j];
for(int t=0;t<4;t++){
int tx=i+dx[t],ty=j+dy[t];
if(can(tx,ty))lj(now,get(tx,ty),inf);
}
}
else {
lj(now,T,mid-s[i][j]);
}
}
}
while(BFS())ans+=lian(S,inf);
return ans==cnt;
}
int main(){
cin>>Te;
while(Te--){
scanf("%d%d",&n,&m);
num1=num2=0;sum1=sum2=0;Max=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%lld",&s[i][j]);
Max=max(Max,s[i][j]);
if((i+j)&1){
num1++,sum1+=s[i][j];
}
else num2++,sum2+=s[i][j];
}
}
if(num1==num2){
l=Max,r=inf;
while(l<r){
int mid=l+r>>1;
if(pd(mid))r=mid;
else l=mid+1;
}
cout<<num1*r-sum1<<endl;
}
else {
long long x=(sum1-sum2)/(num1-num2);
//cout<<x<<sum1<<' '<<sum2<<' '<<num1<<' '<<num2<<endl;
if(pd(x))cout<<num1*x-sum1<<endl;
else puts("-1");
}
}
return 0;
}