BZOJ 2756 SCOI 奇怪的游戏

2756: [SCOI2012]奇怪的游戏

Time Limit: 40 Sec  Memory Limit: 128 MB
Submit: 4978  Solved: 1381
[Submit][Status][Discuss]

Description

Blinker最近喜欢上一个奇怪的游戏。 
这个游戏在一个 N*M 的棋盘上玩,每个格子有一个数。每次 Blinker 会选择两个相邻
的格子,并使这两个数都加上 1。 
现在 Blinker 想知道最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同
一个数则输出-1。 

Input

输入的第一行是一个整数T,表示输入数据有T轮游戏组成。 
每轮游戏的第一行有两个整数N和M, 分别代表棋盘的行数和列数。 
接下来有N行,每行 M个数。 

Output


  对于每个游戏输出最少能使游戏结束的次数,如果永远不能变成同一个数则输出-1。

Sample Input

2
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2

Sample Output

2
-1

HINT

 

【数据范围】 

    对于30%的数据,保证  T<=10,1<=N,M<=8 

对于100%的数据,保证  T<=10,1<=N,M<=40,所有数为正整数且小于1000000000 

 

 

Source

这道题的思路很妙啊

对棋盘进行黑白染色
设黑格个数为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)

 

#include <bits/stdc++.h>
#define ll long long
#define p(x,y) (x-1)*m+y
#define inf (1LL<<50)
using namespace std;
inline int read(){
	int x=0;int f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int MAXN=2e4+10;
const int dx[4]={0,1,-1,0};
const int dy[4]={1,0,0,-1};
struct node{
	int y,next,back;
	ll flow;
}e[MAXN];
int linkk[MAXN],len=0,n,m,level[3000],head,tail,s,t,q[MAXN],v[50][50],col[50][50],num1,num2;
ll sum1,sum2,l,r;
inline void insert(int x,int y,ll f){
	e[++len].y=y;e[len].next=linkk[x];linkk[x]=len;e[len].flow=f;e[len].back=len+1;
	e[++len].y=x;e[len].next=linkk[y];linkk[y]=len;e[len].flow=0;e[len].back=len-1;
}
inline bool getlevel(){
	head=tail=0;
	memset(level,-1,sizeof(level));
	q[++tail]=s;level[s]=0;
	while(head<tail){
		int tn=q[++head];
		for(int i=linkk[tn];i;i=e[i].next){
			if(level[e[i].y]==-1&&e[i].flow){
				level[e[i].y]=level[tn]+1;
				q[++tail]=e[i].y;
			}
		}
	}
	return level[t]>=0;
}
inline ll getmaxflow(int x,ll flow){
	if(x==t) return flow;
	ll f=0,d;
	for(int i=linkk[x];i;i=e[i].next){
		if(level[e[i].y]==level[x]+1&&e[i].flow){
			if(d=getmaxflow(e[i].y,min(flow-f,e[i].flow))){
				f+=d;e[i].flow-=d;e[e[i].back].flow+=d;
				if(f==flow) return f;
			}
		}
	}
	if(f==0) level[x]=-1;
	return f;
}
inline ll dinic(){
	ll ans=0,d;
	while(getlevel()){
		while(d=getmaxflow(s,inf)) ans+=d;
	}
	return ans;
}
inline bool check(ll x){
	memset(linkk,0,sizeof(linkk));
	len=0;ll sum=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(col[i][j]){
				insert(p(i,j),t,x-v[i][j]);
			}
			else{
				insert(s,p(i,j),x-v[i][j]);sum+=x-v[i][j];
				for(int k=0;k<4;k++){
				    int tx=i+dx[k];int ty=j+dy[k];
				    if(tx<1||ty<1||tx>n||ty>m) continue;
				    insert(p(i,j),p(tx,ty),inf);
			    }
			}
		}
	}
	return dinic()==sum;
}
int main(){
	int T=read();
	while(T--){
		n=read();m=read();s=0;t=p(n+1,1);sum1=sum2=num1=num2=0;
		l=0,r=inf;int mx=0;
		memset(col,0,sizeof(col));
	    for(int i=1;i<=n;i++){
		    for(int j=1;j<=m;j++){
			    v[i][j]=read();mx=max(v[i][j],mx);
			    if((i+j)&1) col[i][j]=1,sum1+=v[i][j],num1++;
			    else num2++,sum2+=v[i][j];
		    }
	    }
	    if(num1!=num2){
	 	    ll x=(sum2-sum1)/(num2-num1);
		    if(x>=mx&&check(x)) printf("%lld\n",x*num1-sum1);
		    else printf("-1\n");
	    }
	    else{
		    int mn=-1;l=mx;
		    if(sum1!=sum2){
		    	printf("-1\n");continue;
			}
		    while(l<=r){
			    ll mid=(l+r)>>1;
			    if(check(mid)) mn=mid,r=mid-1;
		  	    else l=mid+1;
		    }
		    if(mn==-1) printf("-1\n");
		    else printf("%lld\n",1LL*mn*num1-sum1);
	    }
	}
	return 0;
}

  

posted @ 2018-05-01 11:24  zhangenming  阅读(185)  评论(0编辑  收藏  举报