題目: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 }