【bzoj2756 奇怪的游戏】

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

【题解】

         ①你有一个很好的愿望:要是能知道最后的棋盘是什么样子就好了!

         ②设棋盘最后都为x(同时和数与形状相关的问题,染色很实用),

           染色后设白格总数num1,和sum1;黑格总数num2,和sum2;我们可以写下这样的式子:
           x*num1-sum1=x*num2-
sum2 (由于选取相邻的两个格+1) 化一化:
           sum1-sum2=x*(num1-num2) 回到初中学过的一元一次方程解的讨论:
           若num1-num2≠0 ,解出x,若比原图中maxn还小,就一定不行,反之check一下;

           若num1-num2=0,若sum1-sum2≠0肯定就没戏了,反之就此方程对应了很多很多解,考虑实际情况我们可以说在这种情况下:x取a成立的话,取a+1一定成立(但是对a和a-1就不一定对了,我就是错在这里的),二分+check即可;

             ③check的话就超源连白色,超汇连黑色,边为差值,对白格向相邻黑格建inf边,看是否满流。

 

  1 /*2
  2 2 2
  3 1 2
  4 2 3
  5 3 3
  6 1 2 3
  7 2 3 4
  8 4 3 2
  9 没去freopen居然调了半个小时,醉了。
 10 inf和INF什么的,最讨厌了。
 11 染色技巧很玄妙! 
 12 */
 13 #include <cstdio>
 14 #include <iostream>
 15 #include <cstring>
 16 #include <algorithm>
 17 #include <queue>
 18 #include <vector>
 19 #include <ctime>
 20 #include <cmath>
 21 #define ll long long
 22 #define N 50
 23 #define mem(f,a) memset(f,a,sizeof(f))
 24 #define Run(i,l,r) for(ll i=l;i<=r;i++)
 25 #define Don(i,l,r) for(ll i=l;i>=r;i--)
 26 #define Eun(i,u,E,head) for(ll i=head[u],v=E[i].v;i!=-1;i=E[i].next,v=E[i].v)
 27 using namespace std;
 28 int n,m;
 29 const ll inf=(1LL<<49);
 30 const ll INF=(1LL<<60);
 31 struct Edge{
 32     int v,next;
 33     ll cap,flow;
 34 }E[1000000];
 35 ll a[N][N],color[N][N];
 36 ll head[N*N],k,cur[N*N],vis[N*N],d[N*N];
 37 int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
 38 queue<int>q;
 39 int idx(int i,int j){return (i-1)*m+j;}
 40 void adde(int u,int v,ll cap)
 41 {    E[k]=(Edge){v,head[u],cap,0};
 42     head[u]=k++;
 43     E[k]=(Edge){u,head[v],0,0};
 44     head[v]=k++;
 45 }
 46 bool Bfs()
 47 {    mem(vis,0); mem(d,0);
 48     vis[0]=1; q.push(0);
 49     while (!q.empty()){
 50         int u=q.front(); q.pop();
 51         Eun(i,u,E,head)if (E[i].cap>E[i].flow&&!vis[v]){
 52             vis[v]=1;
 53             q.push(v);
 54             d[v]=d[u]+1;
 55         }
 56     }
 57     return (vis[n*m+1]);
 58 }
 59 ll Dfs(int u,ll a)
 60 {    if (u==n*m+1||a==0) return a;
 61     ll flow=0,f;
 62     Eun(i,u,E,cur){
 63         cur[u]=i;
 64         if (d[v]==d[u]+1&&(f=Dfs(v,min(E[i].cap-E[i].flow,a)))>0){
 65             flow+=f;
 66             a-=f;
 67             E[i].flow+=f;
 68             E[i^1].flow-=f;
 69         }
 70         if (a==0) break;
 71     }
 72     return flow;
 73 }
 74 ll Dinic()
 75 {    ll flow=0;
 76     while (Bfs()){
 77         Run(i,0,n*m+1) cur[i]=head[i];
 78         flow+=Dfs(0,INF);
 79     }
 80     return flow;
 81 }
 82 bool check(ll x)
 83 {    k=0; mem(head,-1);
 84     ll tot=0;
 85     Run(i,1,n)
 86     Run(j,1,m){
 87         if (color[i][j]) {
 88             tot+=x-a[i][j];
 89             adde(0,idx(i,j),x-a[i][j]);
 90             Run(k,0,3){
 91                 int ni=i+dx[k]; int nj=j+dy[k];
 92                 if (ni>0&&nj>0&&ni<=n&&nj<=m) adde(idx(i,j),idx(ni,nj),INF);
 93             }
 94         }
 95         else adde(idx(i,j),n*m+1,x-a[i][j]);
 96     }
 97     if (tot==Dinic()) return 1;
 98     else return 0;
 99 }
100 int main()
101 {    freopen("game.in","r",stdin);
102     freopen("game.out","w",stdout);
103     int T;
104     scanf("%d",&T);
105     while (T--){
106         scanf("%d%d",&n,&m);
107         ll maxn=0,num1=0,num2=0; ll sum1=0,sum2=0;
108         Run(i,1,n)
109         Run(j,1,m){
110             scanf("%lld",&a[i][j]);
111             maxn=max(maxn,a[i][j]);
112             if ((i+j)&1) color[i][j]=1,sum1+=a[i][j],num1++;
113             else color[i][j]=0,sum2+=a[i][j],num2++;
114         }
115         if (num1==num2) {
116             if (sum1!=sum2) {printf("-1\n"); continue;}
117             ll l=maxn,r=inf,x=0; 
118             while (l<r){
119                 ll mid=(l+r)/2;
120                 if (check(mid)) x=mid,r=mid;
121                 else l=mid+1;    
122             }
123             if (x) printf("%lld\n",1ll*x*num1-sum1);
124             else printf("-1\n");
125         }
126         else {
127             ll temp=(sum1-sum2)/(num1-num2);
128             if (temp>=maxn&&check(temp)) printf("%lld\n",1ll*temp*num1-sum1); 
129             else printf("-1\n");
130         }
131     }
132     return 0;
133 }//by tkys_Austin;
View Code

 

 

 

posted @ 2018-01-01 15:26  小米狐  阅读(303)  评论(0编辑  收藏  举报
TOP