传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2132
又是一道最小割,这道题让我更深刻的理解到了补集转化思想和最小割的许多技巧,朋友们可以先去刷一下bzoj1976,就会有更深刻的感受。
仍然是集合的分配转化为二分图,连到源点和汇点分别代表a方案和b方案,中间点的连边为两个点的c值之和。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define INF 0x3f3f3f3f const int maxn = 10010, maxm = 1000010, maxk = 110; int fir[maxn], edge[maxm], next[maxm], cap[maxm]; int d[maxn], vd[maxn]; int x[maxk][maxk], y[maxk][maxk], z[maxk][maxk], num[maxk][maxk]; int dd[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; int n, m, ans, s, t, cnt, tot; void addedge(int a, int b, int f) { edge[cnt] = b; next[cnt] = fir[a]; fir[a] = cnt; cap[cnt ++] = f; edge[cnt] = a; next[cnt] = fir[b]; fir[b] = cnt; cap[cnt ++] = 0; return; } bool check(int x, int y) { if(x < 1 || y < 1 || x > n || y > m) return false; return true; } int Isap(int r, int aflow) { if(r == t) return aflow; int flow = aflow, f, md = t - 1; for(int i = fir[r]; ~i ; i = next[i]) { int v = edge[i]; if(cap[i]) { if(d[r] == d[v] + 1) { f = min(flow, cap[i]); f = Isap(v, f); cap[i] -= f; cap[i ^ 1] += f; flow -= f; if(d[s] >= t) return aflow - flow; if(!flow) break; } md = min(md, d[v]); } } if(flow == aflow) { vd[d[r]] --; if(!vd[d[r]]) d[s] = t; d[r] = md + 1; vd[d[r]] ++; } return aflow - flow; } int main() { memset(fir, -1, sizeof(fir)); scanf("%d%d", &n, &m); for(int i = 1; i <= n; i ++) { for(int j = 1; j <= m; j ++) { num[i][j] = ++tot; } } s = tot + 1; t = tot + 2; for(int i = 1; i <= n; i ++) { for(int j = 1; j <= m; j ++) { scanf("%d", &x[i][j]); ans += x[i][j]; if((i + j) & 1) addedge(s, num[i][j], x[i][j]); else addedge(num[i][j], t, x[i][j]); } } for(int i = 1; i <= n; i ++) { for(int j = 1; j <= m; j ++) { scanf("%d", &y[i][j]); ans += y[i][j]; if((i + j) & 1) addedge(num[i][j], t, y[i][j]); else addedge(s, num[i][j], y[i][j]); } } for(int i = 1; i <= n; i ++) { for(int j = 1; j <= m; j ++) { scanf("%d", &z[i][j]); } } for(int i = 1; i <= n; i ++) { for(int j = 1; j <= m; j ++) { for(int k = 0; k < 4; k ++) { int tx = i + dd[k][0]; int ty = j + dd[k][1]; if(check(tx, ty)) { ans += z[i][j]; addedge(num[i][j], num[tx][ty], z[i][j] + z[tx][ty]); } } } } vd[0] = t; while(d[s] < t) ans -= Isap(s, INF); printf("%d\n", ans); return 0; }