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;
}
posted @ 2022-07-29 15:09  Samsara-soul  阅读(46)  评论(0编辑  收藏  举报