bzoj 2756 [SCOI2012]奇怪的游戏 二分+网络流

 

2756:[SCOI2012]奇怪的游戏

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

Sample Output

2
-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 }

 

posted @ 2018-03-31 15:27  Kaiser-  阅读(130)  评论(0编辑  收藏  举报