传送门: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;
}