【bzoj2756 奇怪的游戏】
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
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
Sample Output
-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;