1. 程式人生 > 實用技巧 >【二分圖最大點獨立集/最小割】P2774 方格取數問題

【二分圖最大點獨立集/最小割】P2774 方格取數問題

P2774 方格取數問題

思路

騎士共存問題類似。同樣將頂點分為奇數點和偶數點,則可以全部只取奇數點,或全部只取偶數點;若取了一個奇數點\((i,j)\),則受影響的只有一部分偶數點,即無法取得的與其有公共邊的頂點\((i-1,j),(i+1,j),(i,j-1),(i,j+1)\),而所有奇數點都不受影響;偶數點同理。

由此可以抽象成二分圖模型,奇數點為左部點,偶數點為右部點,在互相影響的頂點間連一條有向邊。注意必須從右部點出發,連向左部點。如果反過來連會WA(為什麼?)。然後問題即求二分圖最大獨立集=總點權-最大匹配(最小割).

寫錯一堆細節……

  • 座標雜湊,應該是\((x-1)*m+y\)
    ,不是\(x*n+y\),也不是\((x-1)*n+y\)!!
  • 判斷點是奇數點還是偶數點,是用(i+j)&1,不是solve(i,j)&1!!
  • 頂點內部連邊,只能在連向了匯點的頂點裡遍歷,不能在被源點連向的頂點裡遍歷,前者AC,後者全部WA(為什麼?)

程式碼

const int INF = 0x3f3f3f3f;
const int maxn = 6e2 + 100;
const int maxm = 2e4 + 100;
int cnt_e = 1;
int head[maxn];
int n, m;
int s, t;
int cur[maxn], depth[maxn], gap[maxn];
LL Maxflow = 0;

struct Edge {
    int from, to, next;
    LL w;
}e[maxm];
void addn(int& cnt_e, int head[], Edge e[], int u, int v, LL w) {
    //網路流建圖
    e[++cnt_e].next = head[u]; e[cnt_e].from = u; e[cnt_e].to = v; e[cnt_e].w = w; head[u] = cnt_e;
    e[++cnt_e].next = head[v]; e[cnt_e].from = v; e[cnt_e].to = u; e[cnt_e].w = 0; head[v] = cnt_e;
}

void bfs() {
    mem(depth, -1);
    mem(gap, 0);
    depth[t] = 0;
    gap[0] = 1;
    cur[t] = head[t];
    queue<int> q;
    q.push(t);
    while (q.size()) {
        int u = q.front(); q.pop();
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].to;
            if (depth[v] != -1) continue;
            q.push(v);
            depth[v] = depth[u] + 1;
            gap[depth[v]]++;
        }
    }
    return;
}

LL dfs(int now, LL minflow,int n) {
    if (now == t) {
        Maxflow += minflow;
        return minflow;
    }
    LL nowflow = 0;
    for (int i = cur[now]; i; i = e[i].next) {
        cur[now] = i;
        int v = e[i].to;
        if (e[i].w && depth[v] + 1 == depth[now]) {
            LL k = dfs(v, min(e[i].w, minflow - nowflow), n);
            if (k) {
                e[i].w -= k;
                e[i ^ 1].w += k;
                nowflow += k;
            }
            if (minflow == nowflow) return nowflow;
        }
    }
    gap[depth[now]]--;
    if (!gap[depth[now]]) depth[s] = n + 1;
    depth[now]++;
    gap[depth[now]]++;
    return nowflow;
}

LL ISAP(int n) {
    Maxflow = 0;
    bfs();
    while (depth[s] < n) {
        memcpy(cur, head, sizeof(head));
        dfs(s, INF, n);
    }
    return Maxflow;
}

int solve(int x, int y) {
    return (x - 1) * m + y;
}

int dirx[] = { 0,0,1,-1 };
int diry[] = { 1,-1,0,0 };

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m;
    s = n * m + 1;
    t = s + 1;
    LL sum = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            LL val; cin >> val;
            sum += val;
            if ((i + j) & 1) {
                addn(cnt_e, head, e, s, solve(i, j), val);
            }
            else {
                addn(cnt_e, head, e, solve(i, j), t, val);
                for (int k = 0; k < 4; k++) {
                    int nx = i + dirx[k];
                    int ny = j + diry[k];
                    if (nx<1 || ny<1 || nx>n || ny>m) continue;
                    addn(cnt_e, head, e, solve(i, j), solve(nx, ny), INF);
                }
            }
        }
    }
    ISAP(n * m + 2);
    cout << sum - Maxflow;
    
    return 0;
}