P2774 方格取数问题 题解

题目描述

我总感觉他这个题面描述怪怪的,总之,讲了这样一件事情

从一个(N imes M)的矩阵中取点,取每个点时周边的四个点不能取,求取出点的最大值

P2774 方格取数问题

问题解决

显然我们可以对矩阵黑白染色,同为黑色的点为一个阵营,白色为一个阵营,同一个阵营中的点没有任何影响,不同阵营中的点可以会有牵制

我们反过来考虑牵制

然后就得到了一个类似二分图的东西,考虑一种数据结构,能删掉一个元素,表示不取这个方格,删掉的代价为方格的权值;要么删掉的总是保证策略最优的,要么能反悔;最终状态为:没有互斥的方格了。

自然而然就想到了割

将一个阵营向超级原点建边,另外一个阵营向超级汇点建边,牵制表示建流量为(INF)的边,然后刷最小割(=)最大流就好了

代码实现

#include <cstdio>
#include <cctype>
#include <cstring>
#include <queue>
#define N 10010
#define E 100010
#define S 0
#define T (m * n + 1)
#define code(i, j) ((i - 1) * m + j)//点的线性标号 
#define between(x, flo, top) (flo <= x and x <= top)//您是不是不喜欢这个qwq 
int getint() {
	int res = 0, ch = getchar();
	while (!isdigit(ch) and ch != EOF)
		ch = getchar();
	while (isdigit(ch))
		res = res * 10 + (ch - '0'), ch = getchar();
	return res;
}
inline int min(int x, int y) { return (x < y) ? x : y; }
using std::queue;
const int d[4][2] = {//待会枚举四个方向用的 
	{0, 1},
	{0, -1},
	{1, 0},
	{-1, 0}
};

int m, n;
int sum = 0;

int first[N];
int nxt[E], to[E], val[E], cnt = 1;
void addE(int u, int v, int w) {
	++cnt;
	to[cnt] = v;
	val[cnt] = w;
	nxt[cnt] = first[u];
	first[u] = cnt;
}

int dep[N];
queue<int> q;
bool bfs() {
	memset(dep, 0, sizeof(dep));
	
	dep[S] = 1;
	q.push(S);
	while (not q.empty()) {
		int u = q.front();
		q.pop();
		for (int p = first[u]; p; p = nxt[p]) {
			int v = to[p];
			if (dep[v])
				continue;
			if (val[p]) {//放心,开始都是正权的情况下,不会出现负数的 
				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 p = first[u]; p and in; p = nxt[p]) {
		
		if (val[p] == 0)
			continue;
		int v = to[p];
		if (dep[v] != dep[u] + 1)
			continue;
		
		int res = dfs(v, min(val[p], in));
		val[p] -= res;
		val[p ^ 1] += res;
		in -= res;
		out += res;
	}
	
	return out;
}

int main() {
	n = getint(), m = getint();
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j) {
			int w = 0;
			sum += w = getint();//假定全部都取,随后会删 
			if ((i + j) % 2 == 0) {//阵营A,源点连向自己,自己连向阵营B 
				addE(S, code(i, j), w);
				addE(code(i, j), S, 0);
				
				for (int k = 0; k <= 3; ++k) {
					int x = i + d[k][0], y = j + d[k][1];
					if (between(x, 1, n) and between(y, 1, m)) {
						addE(code(i, j), code(x, y), 2e9);
						addE(code(x, y), code(i, j), 0);
					}
				}				
			}
			else {//阵营B,连向汇点 
				addE(code(i, j), T, w);
				addE(T, code(i, j), 0);
			}
		}
	
	int cut = 0;//最小割 
	while (bfs())
		cut += dfs(S, 2e9);//最小割 = 最大流 
	printf("%d
", sum - cut);
	return 0;
}
原文地址:https://www.cnblogs.com/martian148/p/15185140.html