LOJ#6007. 「网络流 24 题」方格取数
在一个有 \(n\times m\) 个方格的棋盘中,每个方格中有一个正整数 \(v_{i,j}\)。
现要从方格中取数,使任意 \(2\) 个数所在方格没有公共边,求取出的数的最大总和。
\(1\le n,m\le 100,1\le v_{i,j}\le 10^5\)。
首先这 \(n\times m\) 个点构成了一个二分图,求取出的数的最大和等价于求删去的数的最小和。
考虑建立网络流模型,建立一个超级源点 \(S\) 和 一个超级汇点 \(T\),设 \(X=\{(i,j)\mid i+j\equiv 0(\text{mod}\ 2)\},Y=\{(i,j)\mid i+j\equiv 1(\text{mod}\ 2)\}\)。
- \(S\rightarrow x(x\in X)\),边权为 \(v_{x}\);
- \(x(x\in X)\rightarrow y(y\in Y\and y\text{ is a neighbor of }x)\),边权为 \(+\infty\);
- \(y(y\in Y)\rightarrow T\),边权为 \(v_{y}\)。
那么若存在一条从 \(S\) 流向 \(T\) 的路径,就说明该方案不合法。所以问题就是求这个图的最小割。
由于最小割等于最大流,因此直接 \(\text{dinic}\) 跑一遍即可。
#include <bits/stdc++.h>
#define For(i, a, b) for (int i = a; i <= b; ++ i)
#define Rof(i, a, b) for (int i = a; i >= b; -- i)
#define eb emplace_back
using namespace std;
typedef long long ll;
const int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
const int N = 100005, inf = 1e9;
int n, m, cnt = 1, x, to[N], nxt[N], head[N], dep[N], val[N], sum, max_flow;
queue <int> Q;
inline void add(int x, int y, ll w) {
to[++ cnt] = y, nxt[cnt] = head[x], head[x] = cnt, val[cnt] = w;
}
inline bool bfs() {
memset(dep, 0, sizeof dep);
Q.push(0), dep[0] = 1;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
for (int i = head[u]; i; i = nxt[i])
if (val[i] && !dep[to[i]])
dep[to[i]] = dep[u] + 1, Q.push(to[i]);
}
return dep[n * m + 1];
}
inline int dfs(int now, int lim_flow) {
if (now == n * m + 1) return lim_flow;
int out = 0;
for (int i = head[now]; i; i = nxt[i])
if (val[i] && dep[to[i]] == dep[now] + 1) {
ll res = dfs(to[i], min(lim_flow, val[i]));
val[i] -= res, val[i ^ 1] += res;
lim_flow -= res, out += res;
}
if (!out) dep[now] = 0;
return out;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= m; ++ j) {
scanf("%d", &x), sum += x;
if ((i + j) & 1) {
add(0, (i - 1) * m + j, x);
add((i - 1) * m + j, 0, 0);
for (int k = 0; k < 4; ++ k) {
int nx = i + dx[k], ny = j + dy[k];
if (nx && nx <= n && ny && ny <= m) {
add((i - 1) * m + j, (nx - 1) * m + ny, inf);
add((nx - 1) * m + ny, (i - 1) * m + j, 0);
}
}
} else {
add((i - 1) * m + j, n * m + 1, x);
add(n * m + 1, (i - 1) * m + j, 0);
}
}
while (bfs()) max_flow += dfs(0, inf);
return printf("%d\n", sum - max_flow), 0;
}