网络流24题(09)方格取数问题(最大点权独立集 + 最小割最大流)

题意:

在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。

思路:

1. 不清楚何为“最小点权覆盖集”和“最大点权独立集”概念的请看这篇文章《最小割模型在信息学竞赛中的应用》;

2. “最小点权覆盖集”和“最大点权独立集”是两个互补的概念,我们可以利用最小割求一个二分图的最小点权覆盖集;

3. 把黑白相间染色,黑色部分为 X 集,白色部分为 Y 集,s 向 X 中的点引弧, Y 向 t 引弧,容量为 点权的大小;

   X 和 Y 中相邻的点由 X 向 Y 引弧,容量为无穷大,这么做的目的是为了让割边仅在 <s, X> 或 <Y, t> 中;

4. 之所以这么做,是因为我们要求这个图中的“最大点权独立集”,依照其概念可以知道:任意两点不相邻<=>任意两点构成的边不在图中;

5. 求上面网络的最大流,并且根据最小割最大流定理,再根据我们的建图,最大流->最小割->最小点权覆盖。输出 = 总点权 - 最大流;

 

#include <cstdio>

#include <cstring>

#include <iostream>

#include <algorithm>

#include <queue>

#include <vector>

using namespace std;



const int MAXN = 50*50;

const int INFS = 0x7FFFFFFF;



struct edge {

    int from, to, cap, flow;

    edge(int _from, int _to, int _cap, int _flow) 

        : from(_from), to(_to), cap(_cap), flow(_flow) {}

};



class Dinic {

public:

    void initdata(int n, int s, int t) {

        this->n = n, this->s = s, this->t = t;

        edges.clear();

        for (int i = 0; i < n; i++)

            G[i].clear();

    }

    void addedge(int u, int v, int cap) {

        edges.push_back(edge(u, v, cap, 0));

        edges.push_back(edge(v, u, 0, 0));

        G[u].push_back(edges.size() - 2);

        G[v].push_back(edges.size() - 1);

    }

    bool BFS() {

        for (int i = 0; i < n; i++)

            vis[i] = false, d[i] = 0;

        queue<int> Q;

        Q.push(s);

        vis[s] = true;

        while (!Q.empty()) {

            int x = Q.front(); Q.pop();

            for (int i = 0; i < G[x].size(); i++) {

                edge& e = edges[G[x][i]];

                if (e.cap > e.flow && !vis[e.to]) {

                    vis[e.to] = true;

                    d[e.to] = d[x] + 1;

                    Q.push(e.to);

                }

            }

        }

        return vis[t];

    }

    int DFS(int x, int aug) {

        if (x == t || aug == 0) return aug;

        int flow = 0;

        for (int i = 0; i < G[x].size(); i++) {

            edge& e = edges[G[x][i]];

            if (d[e.to] == d[x] + 1) {

                int f = DFS(e.to, min(aug, e.cap - e.flow));

                if (f <= 0) continue;

                e.flow += f;

                edges[G[x][i]^1].flow -= f;

                flow += f;

                aug -= f;

                if (aug == 0) break;

            } 

        }

        return flow;

    }

    int maxflow() {

        int flow = 0;

        while (BFS()) {

            flow += DFS(s, INFS);

        }

        return flow;

    }

private:

    vector<edge> edges;

    vector<int> G[MAXN];

    int n, s, t, d[MAXN];

    bool vis[MAXN];

};



Dinic dc;

int row, col;

int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};



bool check(int x, int y) {

    if (1 <= x && x <= row && 1 <= y && y <= col) {

        return true;

    }

    return false;

}



int main() {

    scanf("%d%d", &row, &col);

    int sum = 0;

    int s = 0, t = row*col + 1;

    dc.initdata(t + 1, s, t);

    for (int i = 1; i <= row; i++) {

        for (int j = 1; j <= col; j++) {

            int a;

            scanf("%d", &a);

            sum += a;

            if ((i+j) & 1)

                dc.addedge((i-1)*col+j, t, a);

            else {

                dc.addedge(s, (i-1)*col+j, a);

                for (int k = 0; k < 4; k++) {

                    int x = i + dir[k][0];

                    int y = j + dir[k][1];

                    if (check(x, y))

                        dc.addedge((i-1)*col+j, (x-1)*col+y, INFS);

                }

            }

        }

    }

    printf("%d\n", sum - dc.maxflow());

    return 0;

}

你可能感兴趣的:(网络流)