最大权闭合子图进阶

Posted natsuka

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大权闭合子图进阶相关的知识,希望对你有一定的参考价值。

(quad)上一篇博客 中简要地讲解了最大权闭合子图。在这一篇博客里,我希望能够写一下最大权闭合子图的一些拓展应用。
(quad)P2774 方格取数问题 为例。

有一个 (m)(n) 列的方格图,每个方格中都有一个正整数。现要从方格中取数,使任意两个数所在方格没有公共边,且取出的数的总和最大,请求出最大的和。

(quad) 这一道题有收益元素,却没有代价元素;关系限制也与一般的最大权闭合子图不一样。最大权闭合子图的关系限制的通式是(i),则一定 (j),而这道题的形式却是(i),则一定不 (j)。然而,把棋盘像国际象棋那样染色,我们却可以发现,若取一个白格,则一定不取它周围的黑格,这样就得到了最大权闭合子图的三要素:

  1. 收益元素:白格
  2. 代价元素:不取黑格
  3. 限制关系:若(取白格),则一定(不取周围的黑格)。
#include <cstdio>
#include <cstring>

#define index ((i - 1) * n + j)

inline int read(void){
    int res = 0; char ch = std::getchar();
    while(ch < '0' || ch > '9')
        ch = std::getchar();
    while(ch >= '0' && ch <= '9')
        res = res * 10 + ch - 48, ch = std::getchar();
    return res;
}

const int MAXN = 1e2 + 19, INF = 0x3f3f3f3f;

struct Queue{
    int q[MAXN * MAXN << 3], head, tail;
    int& operator[](const int& __x){
        return q[__x];
    }
};

struct Edge{
    int to, next, c;
}edge[MAXN * MAXN << 3];

int cnt = -1, head[MAXN * MAXN];

inline void add(int from, int to, int c){
    edge[++cnt].to = to;
    edge[cnt].c = c;
    edge[cnt].next = head[from];
    head[from] = cnt;
}

int m, n, sq[MAXN][MAXN], ans;
int s = 0, t;

int dep[MAXN * MAXN];

int bfs(void){
    static Queue q; q[q.head = q.tail = 0] = s;
    std::memset(dep, 0, sizeof(dep)); dep[s] = 1;
    while(q.head <= q.tail){
        int node = q[q.head++];
        for(int i = head[node]; i != -1; i = edge[i].next)
            if(!dep[edge[i].to] && edge[i].c)
                dep[edge[i].to] = dep[node] + 1, q[++q.tail] = edge[i].to;
    }
    return dep[t];
}

inline int min(const int& a, const int& b){
    return a < b ? a : b;
}

int dfs(int node, int flow){
    if(node == t || !flow)
        return flow;
    int stream = 0, f;
    for(int i = head[node]; i != -1; i = edge[i].next)
        if(dep[edge[i].to] == dep[node] + 1 && (f = dfs(edge[i].to, min(flow, edge[i].c)))){
            flow -= f, stream += f;
            edge[i].c -= f, edge[i ^ 1].c += f;
            if(!flow)
                break;
        }
    return stream;
}

int dinic(void){
    int flow = 0;
    while(bfs())
        flow += dfs(s, INF);
    return flow;
}

int main(){
    std::memset(head, -1, sizeof(head));
    m = read(), n = read();
    t = m * n + 1;
    for(int i = 1; i <= m; ++i)
        for(int j = 1; j <= n; ++j)
            ans += sq[i][j] = read();
    for(int i = 1; i <= m; ++i)
        for(int j = 1; j <= n; ++j)
            if((i + j) & 1){
                add(s, index, sq[i][j]), add(index, s, 0);
                if(i > 1)
                    add(index, index - n, INF), add(index - n, index, 0);
                if(i < m)
                    add(index, index + n, INF), add(index + n, index, 0);
                if(j > 1)
                    add(index, index - 1, INF), add(index - 1, index, 0);
                if(j < n)
                    add(index, index + 1, INF), add(index + 1, index, 0);
            }
            else
                add(index, t, sq[i][j]), add(t, index, 0);
    ans -= dinic();
    printf("%d
", ans);
    return 0;
}

(quad) 通过把取黑格转换为不取黑格,我们巧妙地构造了一个最大权闭合子图。
(quad) 再看另一道题 P2598 [ZJOI2009]狼和羊的故事

(n imes m) 的棋盘上有三种格子:狼,羊,空。请沿着格子的边缘建立最少的篱笆,使得任意两个羊和狼既没有公共边也无法通过空地相连。

(quad) 找到关系限制:若 (i) 是一个狼领地的边,(j) 是一个羊领地的边,且 (i)(j) 经过若干(可以是零)块空地相连,那么(i) 不建篱笆,(j) 就一定建篱笆。收益元素是:(i) 不建篱笆,代价元素是:(j) 建篱笆。这样就转换为最大权闭合子图了。
(quad) 其实以上两题还有别的理解方式,我只是希望能用一个形式化的模板概括它们。

以上是关于最大权闭合子图进阶的主要内容,如果未能解决你的问题,请参考以下文章

最大权闭合子图(最小割)

最大权闭合子图 ( 最大流最小割模型 )

bzoj1497 最大获利(最大权闭合子图)

最大权闭合子图

最大权闭合子图

最大权闭合子图