BYRBT

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

 

题解:

  给格子黑白染色后我们会发现,每次必定是把一个黑格子和一个白格子的值加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啊……………………

 

View Code
  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 }

 

 

 

 

posted @ 2012-04-30 14:26  zhonghaoxi  阅读(1004)  评论(16编辑  收藏  举报
BYRBT