题目:http://61.187.179.132/JudgeOnline/problem.php?id=2756
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
题解:
给格子黑白染色后我们会发现,每次必定是把一个黑格子和一个白格子的值加1,那么就可以把两边给分开了。
设白格子有num1个,数值和为sum1,黑格子有num2 个,数值和为sum2,设所有数最后都变成了x,那么有x*num1-sum1=x*num2-sum2,解之可得x=(sum1-sum2)/(num1-num2),当num1=num2即有偶数个格子时,此方程无意义,所以先说num1!=num2的情况。
num1!=num2时,解出一个x,由于始终是把两个格子的值加1,所以若有解,x不可能小于所有值中的最大值且x必须是个整数,那么这样就只需检验当x满足上述两个条件时,是否可行即可了。
num1=num2时,如果说都变成x可以实现,那么都变成x+1也能实现(做法是黑白格子两两配对后加1),所以可以二分最后变成的那个数即可(所操作的次数与x的大小是正相关的)。
那么现在只需要检验当能否所有数都变成x,自然而然地就想到网络流了。总源向每个白格子连边,每个黑格子向总汇连边,流量为x减去该格子本来的权值,然后相邻的黑白格子连流量为无限大的边,求个最大流max_flow,并且提前算出若该方案可行需要多少步step,如果max_flow=step,则说明这是一个可行解,否则为不可行。
网络流建议写非递归的,递归的跑起来有点慢(虽说应该不会有太大影响的,但我的递归的T了),然后左右界稍微卡好一点,不然也容易T。
考试的时候已经把方法都想出来了,可是不知道又哪里写错了,再次崩掉。唉,还得努力攒RP啊……………………

1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<queue>
5
6 using namespace std; 7
8 const int maxw=41; 9 const int maxn=1800; 10 const int maxm=100000; 11 const long long INF=123456789876ll; 12
13 int n,m,en,d[maxn],s,e,stack[maxn]; 14
15 long long map[maxw][maxw]; 16
17 queue<int> que; 18
19 struct edge 20 { 21 int e; 22 long long f; 23 edge *next,*op; 24 }*v[maxn],ed[maxm],*p[maxn],*fe[maxn]; 25
26 void add_edge(int s,int e,long long f) 27 { 28 en++; 29 ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;v[s]->f=f; 30 en++; 31 ed[en].next=v[e];v[e]=ed+en;v[e]->e=s;v[e]->f=0; 32 v[s]->op=v[e];v[e]->op=v[s]; 33 } 34
35 int get(int x,int y) 36 { 37 return (x-1)*m+y; 38 } 39
40 void get_add(int x,int y) 41 { 42 if (x>1) add_edge(get(x,y),get(x-1,y),INF); 43 if (x<n) add_edge(get(x,y),get(x+1,y),INF); 44 if (y>1) add_edge(get(x,y),get(x,y-1),INF); 45 if (y<m) add_edge(get(x,y),get(x,y+1),INF); 46 } 47
48 bool bfs() 49 { 50 memset(d,-1,sizeof(d)); 51 d[s]=0; 52 que.push(s); 53 while (que.size()) 54 { 55 int now=que.front(); 56 que.pop(); 57 for (edge *e=v[now];e;e=e->next) 58 if (e->f && d[e->e]==-1) 59 { 60 d[e->e]=d[now]+1; 61 que.push(e->e); 62 } 63 } 64
65 return d[e]!=-1; 66 } 67
68 long long dfs(int now,long long cur_flow) 69 { 70 if (now==e) return cur_flow; 71 long long rest=cur_flow; 72 for (edge *e=v[now];e;e=e->next) 73 if (e->f && d[e->e]==d[now]+1) 74 { 75 long long new_flow=dfs(e->e,min(rest,e->f)); 76 e->f-=new_flow; 77 e->op->f+=new_flow; 78 rest-=new_flow; 79 } 80 return cur_flow-rest; 81 } 82
83 long long agument() 84 { 85 int now,next,stop; 86 long long delta,ans=0; 87 for (int a=s;a<=e;a++) 88 p[a]=v[a]; 89 stack[stop=1]=s; 90 int t=e; 91 while (stop>0) 92 { 93 now=stack[stop]; 94 if (now!=t) 95 { 96 for (;p[now];p[now]=p[now]->next) 97 if ((p[now]->f) && (d[now]+1==d[next=p[now]->e])) break; 98 if (p[now]) 99 { 100 stack[++stop]=next; 101 fe[stop]=p[now]; 102 } 103 else
104 { 105 stop--; 106 d[now]=-1; 107 } 108 } 109 else
110 { 111 delta=INF; 112 for (int a=stop;a>=2;a--) 113 if (fe[a]->f<delta) delta=fe[a]->f; 114 ans+=delta; 115 for (int a=stop;a>=2;a--) 116 { 117 fe[a]->f-=delta; 118 fe[a]->op->f+=delta; 119 if (fe[a]->f==0) stop=a-1; 120 } 121 } 122 } 123 return ans; 124 } 125
126 long long dinic() 127 { 128 long long ans=0; 129 while (bfs()) 130 ans+=agument(); 131 return ans; 132 } 133
134 bool check(long long x) 135 { 136 en=0; 137 memset(v,0,sizeof(v)); 138 s=0; 139 e=n*m+1; 140 long long flow=0; 141 for (int a=1;a<=n;a++) 142 for (int b=1;b<=m;b++) 143 { 144 if ((a+b)%2==0) 145 { 146 add_edge(s,get(a,b),x-map[a][b]); 147 get_add(a,b); 148 } 149 else add_edge(get(a,b),e,x-map[a][b]); 150 flow+=x-map[a][b]; 151 } 152 long long ans=dinic(); 153 if (ans*2!=flow) return false; 154 else return true; 155 } 156
157 long long gettime(long long x) 158 { 159 long long ans=0; 160 for (int a=1;a<=n;a++) 161 for (int b=1;b<=m;b++) 162 ans+=x-map[a][b]; 163 return ans>>1; 164 } 165
166 int main() 167 { 168 freopen("game.in","r",stdin); 169 freopen("game.out","w",stdout); 170
171 int t; 172 scanf("%d",&t); 173 for (int z=1;z<=t;z++) 174 { 175 scanf("%d%d",&n,&m); 176 int num1=0,num2=0; 177 long long sum1=0,sum2=0; 178 long long max_v=0; 179 for (int a=1;a<=n;a++) 180 for (int b=1;b<=m;b++) 181 { 182 scanf("%lld",&map[a][b]); 183 if ((a+b)%2==0) 184 { 185 num1++; 186 sum1+=map[a][b]; 187 } 188 else
189 { 190 num2++; 191 sum2+=map[a][b]; 192 } 193 max_v=max(max_v,map[a][b]); 194 } 195 if (num1!=num2) 196 { 197 if ((sum1-sum2) % (num1-num2)!=0) 198 { 199 printf("-1\n"); 200 continue; 201 } 202 else
203 { 204 long long x=(sum1-sum2)/(num1-num2); 205 if (x<max_v) 206 { 207 printf("-1\n"); 208 continue; 209 } 210 if (check(x)) printf("%lld\n",gettime(x)); 211 else printf("-1\n"); 212 } 213 } 214 else
215 { 216 long long l=max_v-1,r=INF; 217 while (l+1!=r) 218 { 219 long long m=(l+r)>>1; 220 if (check(m)) r=m; 221 else l=m; 222 } 223 if (check(r)) printf("%lld\n",gettime(r)); 224 else printf("-1\n"); 225 } 226 } 227
228 return 0; 229 }