SCOI 2012 奇怪的游戏
题目: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啊……………………
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }