码迷,mamicode.com
首页 > 其他好文 > 详细

【网络流24题】方格取数问题

时间:2020-02-17 11:58:23      阅读:24      评论:0      收藏:0      [点我收藏+]

标签:dep   push   ++   set   code   memset   覆盖   表示   else   

题目链接

题面

题目描述

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

输入格式

第一行是两个用空格隔开的整数,分别代表方格图的行数 $m$ 和列数 $n$

第 $2$ 到第  $(m + 1)$$(m + 1)$ 行,每行 $n$ 个整数,第 $(i + 1)$$(i + 1)$ 行的第 $j$个整数代表方格图第 $i$ 行第 $j$ 列的的方格中的数字 $a{i,j}$ 

 

输出格式

输出一行一个整数,代表和最大是多少。

分析&做法

前置:

二分图最大点权独立集:选取一些点使得任意两点间没有边相连

二分图最小点权覆盖集:选取一些点使得任意一条边的两个端点至少有一个被选中

大点权独立集 == 所有点权和 - 最小点权覆盖集

  • 最大点权独立集问题(大点权独立集 == 所有点权和 - 最小点权覆盖集 == 所有点权和 - 最小割 == 所有点权和 - 最大流)
  • 根据边不相邻,我们可以把所有格子a[i,j]染成两种颜色,白格子(i + j mod 2 == 1)和黑格子(i + j mod 2 == 0),相同颜色的格子之间没有相邻的边
  • S向白格子连边,黑格子向T连边(反之亦可,后面都反过来),边权为该点的点权
  • 每一个白格子向他的相邻的上下左右四个黑格子连边,边权为inf
  • 所有的格点构成一个二分图
  • 问题转化成求该图的最大点权独立集,即所有点权和 - 最大流

为什么最大点权独立集 == 所有点权和 - 最小点权覆盖集 == 所有点权和 - 最小割呢?

一、"最大点权独立集 == 所有点权和 - 最小点权覆盖集" :

  • 可以发现任意一个最小点权覆盖集的补集就是最大点权独立集。

二、"所有点权和 - 最小点权覆盖集 == 所有点权和 - 最小割" : 

  • 因为二分图中间的边权为inf,最小割不可能割掉中间的边,只能割掉与源点和汇点相连的边,表示选择了该数
  • [S,T]的一个割就表示去掉这些边,使得没有一条能从S到T的路径,也就是说任意一条中间的边,两个端点连向S或是T的两条边至少有一个被割掉,即两个端点至少有一个点被选中
  • 那么最小割就是最小点权全覆盖集

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define INF 2e9
#define p(i, j) (i - 1) * M + j
using namespace std;

const int _ = 10005;

int N, M, S, T, Ans;

struct Edge {
    int nxt, dis, to;
}e[1000005]; 
int cnte = 1, head[_];

inline void add_Edge(int i, int j, int k) {
    e[++cnte].dis = k, e[cnte].nxt = head[i], e[cnte].to = j, head[i] = cnte;
    e[++cnte].dis = 0, e[cnte].nxt = head[j], e[cnte].to = i, head[j] = cnte;
}

int dep[_];
bool bfs() {
    queue <int> q;
    memset(dep, 0, sizeof(dep));
    dep[S] = 1, q.push(S);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(int v, i = head[u]; i; i = e[i].nxt) {
            if(e[i].dis && !dep[v = e[i].to]) {
                dep[v] = dep[u] + 1, q.push(v);
            }
        }
    }
    return dep[T];
}
int dfs(int u, int in) {
    if(u == T) return in;
    int out = 0;
    for(int v, i = head[u]; i && in; i = e[i].nxt) {
        if(e[i].dis && dep[v = e[i].to] == dep[u] + 1) {
            int tmp = dfs(v, min(in, e[i].dis));
            e[i].dis -= tmp, e[i ^ 1].dis += tmp,
            in -= tmp, out += tmp;
        }
    }
    if(!out) dep[u] = 0;
    return out;
}
int main() {
    cin >> N >> M;
    S = N * M + 1, T = N * M + 2;
    for(int i = 1; i <= N; ++i) {
        for(int x, j = 1; j <= M; ++j) {
            cin >> x, Ans += x;
            if((i + j) & 1) {
                add_Edge(S, p(i, j), x);
                if(i > 1) add_Edge(p(i, j), p(i - 1, j), INF);
                if(i < N) add_Edge(p(i, j), p(i + 1, j), INF);
                if(j > 1) add_Edge(p(i, j), p(i, j - 1), INF);
                if(j < M) add_Edge(p(i, j), p(i, j + 1), INF);
            } 
            else add_Edge(p(i, j), T, x);
        }
    }    
    while(bfs()) Ans -= dfs(S, INF);
    cout << Ans << \n;

    return 0;
}

 

【网络流24题】方格取数问题

标签:dep   push   ++   set   code   memset   覆盖   表示   else   

原文地址:https://www.cnblogs.com/blog-fgy/p/12320743.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有 京ICP备13008772号-2
迷上了代码!