方格取数问题

题目链接:

点我

题目分析:

网络流
考虑先将棋盘黑白染色变成二分图,这样使得每个格子有公共边的其他格子和它自己都是异色的,然后把它向其他四个方向的点连一下边,边容量\(INF\)\(S\)连黑点,容量为权值,白点连\(T\),容量同理
于是问题转化为一个最小割问题

代码:

#include<bits/stdc++.h>
#define INF (1000000000 + 7)
#define N (100000 + 10)
using namespace std;
inline int read() {
	int cnt = 0, f= 1;char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
	while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + (c ^ 48); c = getchar();}
	return cnt * f;
}
const int fx[5] = {0, 1, 0, -1, 0};
const int fy[5] = {0, 0, -1, 0, 1};
int m, n, s, t, first[N << 2], nxt[N << 2], to[N << 2], flow[N], tot = 1, dep[N], cnt[N], ans;
void add(int x, int y, int z) {nxt[++tot] = first[x], first[x] = tot, to[tot] = y, flow[tot] = z;}
int Map[110][110];
queue <int> q;
void build () {
	for (register int i = 1; i <= m; ++i) 
		for (register int j = 1; j <= n; ++j) {
			if ((i + j) & 1) add(s, (i - 1) * n + j, Map[i][j]), add((i - 1) * n + j, s, 0);
			else add(t, (i - 1) * n + j, 0), add((i - 1) * n + j, t, Map[i][j]);
		}
	for (register int i = 1; i <= m; ++ i)
		for (register int j = 1; j <= n; ++j) {
			if ((i + j) & 1) {
				for (register int k = 1; k <= 4; ++k) {
					int x = i + fx[k], y = j + fy[k];
					if (x < 1 || y < 1 || x > m || y > n) continue;
					add((i - 1) * n + j, (x - 1) * n + y, INF);
					add((x - 1) * n + y, (i - 1) * n + j, 0);
				}
			}
		}
}
void bfs_(int t) {
	memset(dep, 0xff, sizeof dep);
	dep[t] = 0, cnt[0] = 1;
	q.push(t);
	while (!q.empty()) {
		int p = q.front(); q.pop();
		for (register int i = first[p]; i; i = nxt[i]) {
			int v = to[i];
			if (dep[v] == -1) {
				++cnt[dep[v] = dep[p] + 1];
				q.push(v);
			}
		}
	}
}
int max_flow;
int dfs_(int p, int f) {
	if (p == t) {max_flow += f; return f;}
	int u = 0;
	for (register int i = first[p]; i; i = nxt[i]) {
		int v = to[i];
		if (flow[i] && dep[v] == dep[p] - 1) {
			int uu = dfs_(v, min(flow[i], f - u));
			if (uu) {
				flow[i] -= uu, flow[i ^ 1] += uu, u += uu;
			}
			if (u >= f) return u;
		}
	}
	if (!--cnt[dep[p]]) dep[s] = m * n + 3;
	++cnt[++dep[p]];
	return u;
}
int main() {
	m = read(), n = read();
	s = m * n + 1, t = m * n + 2;
	for (register int i = 1; i <= m; ++i) 
		for (register int j = 1; j <= n; ++j) 
			Map[i][j] = read(), ans += Map[i][j];
	build();
	bfs_(t);
	while (dep[s] < m * n + 3) dfs_(s, INF);
	printf("%d", ans - max_flow);
	return 0;
}
posted @ 2019-11-12 08:28  kma_093  阅读(149)  评论(0编辑  收藏  举报