网络流24题方格取数问题
Posted blog-fgy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络流24题方格取数问题相关的知识,希望对你有一定的参考价值。
题面
题目描述
有一个 $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 << ‘ ‘; return 0; }
以上是关于网络流24题方格取数问题的主要内容,如果未能解决你的问题,请参考以下文章