zoukankan      html  css  js  c++  java
  • bzoj 2756奇怪的游戏

    2756: [SCOI2012]奇怪的游戏

    Time Limit: 40 Sec  Memory Limit: 128 MB
    Submit: 2571  Solved: 685
    [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 

    考虑末状态有所有数都相等的很好性质,所以设所有数的变为x。因为每次只会对相邻两个格子加1,所以奇偶分治后,两种格子的变化量相同。

    所以可以列出方程

    这里借用黄学长的博客

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

    方法:利用末状态性质,设未知数,列出方程。然后分类讨论,首先要满足方程才有解,然后可以二分答案,在check。其实如果都合法,可以直接二分末状态,然后check。方程讨论是因为解得情况在不同条件下不同。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define maxn 2020
    #define maxm 8020
    #define INF 100000000000000ll
    
    typedef long long LL;
    struct node{
    	int next,to;
    	LL f;
    }e[maxm * 2];
    int head[maxn],cnt = 1,src,sink;
    int q[maxn],hh,tt,dis[maxn],cur[maxn];
    int n,m,T,num1,num2,mx;
    LL sum1,sum2,maxflow,x,ans;
    int dt[maxn][maxn];
    
    inline void adde(int x,int y,LL c){
    	e[++cnt].to = y;
    	e[cnt].next = head[x];
    	e[cnt].f = c;
    	head[x] = cnt;
    	e[++cnt].to = x;
    	e[cnt].next = head[y];
    	head[y] = cnt;
    }
    inline bool bfs(){
    	tt = hh = 0;
    	memset(dis,0,sizeof(dis));
    	q[tt++] = src , dis[src] = 1;
    	while ( hh < tt ){
    		int now = q[hh++];
    		for (int i = head[now] ; i ; i = e[i].next){
    			if ( e[i].f && !dis[e[i].to] ){
    				q[tt++] = e[i].to;
    				dis[e[i].to] = dis[now] + 1;
    			}
    		}
    	}
    	return dis[sink] > 0;
    }
    LL dfs(int now,LL delta){
    	if ( now == sink || !delta ) return delta;
    	LL ret = 0;
    	for (int &i = cur[now] ; i ; i = e[i].next){
    		if ( e[i].f && dis[now] + 1 == dis[e[i].to] ){
    			LL d = dfs(e[i].to,min(delta,e[i].f));
    			ret += d , delta -= d;
    			e[i].f -= d, e[i ^ 1].f += d;
    			if ( !delta ) return ret;
    		}
    	}
    	if ( delta ) dis[now] = -1;
    	return ret;
    } 
    inline LL dinic(){
    	LL flow = 0;
    	while ( bfs() ){
    		for (int i = 1 ; i <= sink ; i++) cur[i] = head[i];
    		flow += dfs(src,INF);
    	}
    	return flow;
    }
    inline void init(LL x){
    	memset(head,0,sizeof(head));
    	memset(e,0,sizeof(e));
    	cnt = 1;
    	for (int i = 1 ; i <= n ; i++){
    		for (int j = 1 ; j <= m ; j++){
    			if ( (i + j) & 1 ){
    				adde((i - 1) * m + j,sink,x - (LL)dt[i][j]);
    			}
    			else{
    				int now = (i - 1) * m + j;
    				adde(src,(i - 1) * m + j,x - (LL)dt[i][j]);
    				if ( i > 1 ) adde(now,now - m,INF);
    				if ( i < n ) adde(now,now + m,INF);
    				if ( j > 1 ) adde(now,now - 1,INF);
    				if ( j < m ) adde(now,now + 1,INF);
    			}
    		}
    	}
    }
    int main(){
    	freopen("input.txt","r",stdin);
    	scanf("%d",&T);
    	while ( T-- ){
    		ans = sum1 = sum2 = 0 , mx = num1 = num2 = 0;
    		scanf("%d %d",&n,&m);
    		src = n * m + 1 , sink = n * m + 2;
    		for (int i = 1 ; i <= n ; i++){
    			for (int j = 1 ; j <= m ; j++){
    				scanf("%d",&dt[i][j]);
    				if ( (i + j) & 1 ) sum2 += (LL)dt[i][j] , num2++;
    				else sum1 += (LL)dt[i][j], num1++;
    				mx = max(dt[i][j],mx);
    			}
    		}
    		if ( num1 != num2 ){
    			if ( ((sum1 - sum2) % (LL)(num1 - num2)) != 0 ){
    				printf("-1
    ");
    				continue;
    			}	
    			x = (sum1 - sum2) / (LL)(num1 - num2);
    			if ( x < mx ){
    				printf("-1
    ");
    				continue;
    			}
    			init(x);
    			maxflow = dinic();
    			if ( maxflow + sum1 == (LL)num1 * x ){
    				printf("%lld
    ",maxflow);
    			}
    			else printf("-1
    ");
    		}
    		else{
    			LL l = mx , r = INF;
    			//二分把当前的每个数都变成mid的情况
    			for (int i = 1 ; i <= 50 ; i++){
    				maxflow = 0;
    				LL mid = (l + r) >> 1;
    				init(mid);
    				maxflow = dinic();
    				if ( maxflow + sum1 == (LL) num1 * mid ){
    					ans = maxflow , r = mid;
    				}
    				else l = mid + 1;
    			}
    			if ( !ans ) printf("-1
    ");
    			else printf("%lld
    ",ans);
    		}
    	}
    	return 0;
    }
  • 相关阅读:
    Eclipse EE 3.6 failed to create the java virtual machine 解决
    [收藏]家用三线插座(220V单相)正确接线方法
    Java设计模式 Design Pattern:包装模式 Decorator Pattern
    MIME 类型列表
    面试题参考
    循环队列的运用求K阶斐波那契序列
    加密技术仿射密码
    面试题集锦_1
    栈的运用(5)转换逆波兰式
    栈的运用(6)逆波兰式求值
  • 原文地址:https://www.cnblogs.com/zqq123/p/5263783.html
Copyright © 2011-2022 走看看