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;
    }
  • 相关阅读:
    Android 解决小米手机Android Studio安装app 报错的问题It is possible that this issue is resolved by uninstalling an existi
    Android Unresolved Dependencies
    Android studio 自定义打包apk名
    Android Fragment与Activity交互的几种方式
    魅族和三星Galaxy 5.0webView 问题Android Crash Report
    Android几种常见的多渠道(批量)打包方式介绍
    Android批量打包 如何一秒内打完几百个apk渠道包
    上周热点回顾(9.30-10.6)团队
    上周热点回顾(9.23-9.29)团队
    上周热点回顾(9.16-9.22)团队
  • 原文地址:https://www.cnblogs.com/zqq123/p/5263783.html
Copyright © 2011-2022 走看看