bzoj 2756 [SCOI2012]奇怪的游戏 二分+网络流
2756:[SCOI2012]奇怪的游戏
Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 4926 Solved: 1362
[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
2 2
1 2
2 3
3 3
1 2 3
2 3 4
4 3 2
Sample Output
2
-1
-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)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 7 #define inf (1LL<<50) 8 #define pa pair<int,int> 9 #define ll long long 10 #define p(x,y) (x-1)*m+y 11 using namespace std; 12 int read() 13 { 14 int x=0,f=1;char ch=getchar(); 15 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 16 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 17 return x*f; 18 } 19 20 ll s0,s1; 21 int c0,c1; 22 int test,n,m,cnt,S,T; 23 int xx[4]={0,0,1,-1},yy[4]={1,-1,0,0}; 24 int a[45][45]; 25 int last[2005],h[2005],q[2005],cur[2005]; 26 bool color[45][45]; 27 struct edge{ 28 int to,next;ll v; 29 }e[20005]; 30 31 void insert(int u,int v,ll w) 32 { 33 e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;e[cnt].v=w; 34 e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;e[cnt].v=0; 35 } 36 bool bfs() 37 { 38 int head=0,tail=1; 39 memset(h,-1,sizeof(h)); 40 q[0]=S;h[S]=0; 41 while(head!=tail) 42 { 43 int now=q[head];head++; 44 for(int i=last[now];i;i=e[i].next) 45 if(e[i].v&&h[e[i].to]==-1) 46 { 47 h[e[i].to]=h[now]+1; 48 q[tail++]=e[i].to; 49 } 50 } 51 return h[T]!=-1; 52 } 53 ll dfs(int x,ll f) 54 { 55 if(x==T)return f; 56 ll w,used=0; 57 for(int i=cur[x];i;i=e[i].next) 58 if(h[e[i].to]==h[x]+1) 59 { 60 w=dfs(e[i].to,min(f-used,e[i].v)); 61 e[i].v-=w;e[i^1].v+=w; 62 if(e[i].v)cur[x]=i; 63 used+=w;if(used==f)return f; 64 } 65 if(!used)h[x]=-1; 66 return used; 67 } 68 ll dinic() 69 { 70 ll tmp=0; 71 while(bfs()) 72 { 73 for(int i=S;i<=T;i++)cur[i]=last[i]; 74 tmp+=dfs(S,inf); 75 } 76 return tmp; 77 } 78 bool check(ll x) 79 { 80 memset(last,0,sizeof(last)); 81 cnt=1;S=0;T=n*m+1; 82 ll tot=0; 83 for(int i=1;i<=n;i++) 84 for(int j=1;j<=m;j++) 85 if(color[i][j]) 86 { 87 insert(S,p(i,j),x-a[i][j]);tot+=x-a[i][j]; 88 for(int k=0;k<4;k++) 89 { 90 int nowx=i+xx[k],nowy=j+yy[k]; 91 if(nowx<1||nowy<1||nowx>n||nowy>m)continue; 92 insert(p(i,j),p(nowx,nowy),inf); 93 } 94 } 95 else insert(p(i,j),T,x-a[i][j]); 96 if(dinic()==tot)return 1; 97 return 0; 98 } 99 int main() 100 { 101 test=read(); 102 while(test--) 103 { 104 c0=c1=s0=s1=0; 105 n=read();m=read(); 106 int mx=0; 107 for(int i=1;i<=n;i++) 108 for(int j=1;j<=m;j++) 109 { 110 a[i][j]=read(),color[i][j]=(i+j)&1; 111 mx=max(mx,a[i][j]); 112 } 113 for(int i=1;i<=n;i++) 114 for(int j=1;j<=m;j++) 115 if(color[i][j])s1+=a[i][j],c1++; 116 else s0+=a[i][j],c0++; 117 if(c0!=c1) 118 { 119 ll x=(s0-s1)/(c0-c1); 120 if(x>=mx) 121 if(check(x)) 122 { 123 printf("%lld\n",x*c1-s1); 124 continue; 125 } 126 puts("-1"); 127 } 128 else 129 { 130 if(s0!=s1) 131 { 132 puts("-1"); 133 continue; 134 } 135 ll l=mx,r=inf; 136 while(l<=r) 137 { 138 ll mid=(l+r)>>1; 139 if(check(mid))r=mid-1; 140 else l=mid+1; 141 } 142 printf("%lld\n",(ll)l*c1-s1); 143 } 144 } 145 }