网络流,最大权独立点集

题意:

图片说明
##分析:
看到这一题,我的第一反应是二分图求最大独立点集。想要用HK算法干掉他。但是仔细一看并非如此,题目让球的是最大权独立点集。这就是完全不同的问题了。

真正的解法推荐看论文:胡伯涛《最小割模型在信息学竞赛中的应用》,直接跳到第五部分读就行了。

不得不感叹,真是巧妙。我们只要建图起点和左半边点连边权值为点权值,做半边点和右半边点连边权值为inf,右半边点和终点连边权值为点权值。再求其最小割,也就是最大流。我们就得到了最小权点覆盖集。然后根据点覆盖集和点独立集的互补性。
我们用总权值-最小点权覆盖集的权值就得到了最大点权独立集的权值。

代码如下:

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int max_n = 55;
const int max_m = 55;
const int inf = 2e9;
int n, m;
int start = 2501, ed = 2502;

struct edge
{
    int to, cap, rev, next;
}E[max_n * max_m * 16];
int head[max_n * max_m];
int cnt = 1;
void add(int from, int to, int cap) {
    E[cnt].to = to;
    E[cnt].cap = cap;
    E[cnt].rev = cnt + 1;
    E[cnt].next = head[from];
    head[from] = cnt++;
    E[cnt].to = from;
    E[cnt].cap = 0;
    E[cnt].rev = cnt - 1;
    E[cnt].next = head[to];
    head[to] = cnt++;
}
void init() {
    fill(head, head + max_n * max_m, false);
    cnt = 1;
}


int dist[max_n * max_m];
int iter[max_n * max_m];
bool searchpath(int s, int t) {
    fill(dist, dist + max_n * max_m, -1);
    dist[s] = 0;queue<int> que;
    que.push(s);
    while (!que.empty()) {
        int u = que.front();que.pop();
        for (int i = head[u];i;i = E[i].next) {
            edge& e = E[i];
            if (dist[e.to] != -1 || e.cap == 0)continue;
            dist[e.to] = dist[u] + 1;
            que.push(e.to);
        }
    }return dist[t] != -1;
}
int matchpath(int u,int t,int f) {
    if (u == t)return f;
    for (int& i = iter[u];i;i = E[i].next) {
        edge& e = E[i];
        if (dist[e.to] <= dist[u] || e.cap == 0)continue;
        int d = matchpath(e.to, t, min(f, e.cap));
        if (d <= 0)continue;
        e.cap -= d;
        E[e.rev].cap += d;
        return d;
    }return false;
}
int dinic(int s,int t) {
    int flow = 0;int f;
    while (searchpath(s, t)) {
        for (int i = 1;i <= ed;i++)iter[i] = head[i];
        while (f = matchpath(s, t, inf))
            flow += f;
    }return flow;
}

int main() {
    ios::sync_with_stdio(0);
    while (cin >> m >> n) {
        int cap;int sum = 0;init();
        for (int i = 0;i < m;i++) {
            for (int j = 1;j <= n;j++) {
                cin >> cap;sum += cap;
                if ((i + j) & 1) {
                    add(start, i * n + j, cap);
                    if (i != 0)add(i * n + j, i * n - n + j, inf);
                    if (i != m - 1)add(i * n + j, i * n + n + j, inf);
                    if (j != 1)add(i * n + j, i * n + j - 1, inf);
                    if (j != n)add(i * n + j, i * n + j + 1, inf);
                }
                else add(i * n + j, ed, cap);
            }
        }cout << sum - dinic(start, ed) << endl;
    }
}