[题解] BZOJ3894 文理分科

题目链接

题目描述

\(n\)\(m\) 列的矩阵,每个人可以选文科或者理科。第 \(i,j\) 个人选文科贡献为 \(a_{i,j}\),选理科贡献为 \(b_{i,j}\),周围及自己选文科贡献为 \(c_{i,j}\),周围及自己选理科贡献为 \(d_{i,j}\)

思路

建图方法见代码。

利用最大权闭合子图,考虑全选文科,初始值为 \(\sum a_{i,j}+c_{i,j}\)

那么理科的贡献就为 \(b_{i,j}\),全选理科的贡献为 \(d_{i,j}\)

不选文科的贡献为 \(-a_{i,j}\)\(5\) 人之中有一个不选的贡献为 \(-c_{i,j}\)

则内部构图中,有三个约束条件:

  • 选了理科,则必不选文科。
  • 选了 \(d_{i,j}\),则四周及自己都必须选理科。
  • 选了一个不选文科,则与这个点相关的 \(c_{i,j}\) 都必须破坏,及必选相关的 \(-c_{i,j}\)

则答案为 \((\sum a_{i,j}+b_{i,j}+c_{i,j}+d_{i,j})-mincut\)

Code

#include <cstdio>
#define INF 0x3f3f3f3f
const int MAXN = 2e5 + 5;
const int MAXM = 1e6 + 5;
struct Edge { int To, Cap, Next; } edge[MAXM << 1];
int head[MAXN], tot = 1;
void Addedge(int u, int v, int w) {
	edge[++tot].Next = head[u], edge[tot].To = v, edge[tot].Cap = w, head[u] = tot;
	edge[++tot].Next = head[v], edge[tot].To = u, edge[tot].Cap = 0, head[v] = tot;
}
int cur[MAXN], dep[MAXN], que[MAXN], qhead, qtail;
int n, m, s, t;
int addx[] = {0, 0, 1, -1, 0};
int addy[] = {1, -1, 0, 0, 0};
int ans;
int Min(int x, int y) { return x < y ? x : y; }
bool bfs(bool limit) {
	for(int i = s; i <= t; i++) dep[i] = 0, cur[i] = head[i];
	qhead = 1; qtail = 1; que[1] = t; dep[t] = 1;
	while(qhead <= qtail) {
		int u = que[qhead++];
		for(int i = head[u]; i; i = edge[i].Next) {
			int v = edge[i].To;
			if(!dep[v] && edge[i ^ 1].Cap && (!limit || !(i & 1))) {
				dep[v] = dep[u] + 1;
				que[++qtail] = v;
				if (v == s) return 1;
			}
		}
	}
	return 0;
}
int dfs(int u, int flow) {
	if(u == t || !flow) return flow;
	int rest = flow;
	for(int i = cur[u]; i && rest; i = edge[i].Next) {
		cur[u] = i;
		int v = edge[i].To;
		if(dep[v] == dep[u] - 1 && edge[i].Cap) {
			int del = dfs(v, Min(rest, edge[i].Cap));
			rest -= del; edge[i].Cap -= del; edge[i ^ 1].Cap += del;
			if(!del) dep[v] = -2;
		}
	}
	return flow - rest;
}
int Dinic() {
	int res = 0, flow;
	while (bfs(1)) while ((flow = dfs(s, INF))) res += flow;
	while (bfs(0)) while ((flow = dfs(s, INF))) res += flow;
	return res;
}
int Get(int x, int y, int h) {
	return (x - 1) * m + y + n * m * h;
}
int main() {
	scanf("%d %d", &n, &m);
	s = 0, t = 4 * n * m + 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 1, a; j <= m; j++) {
			scanf("%d", &a);
			Addedge(Get(i, j, 0), t, a);
			ans += a;
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1, a; j <= m; j++) {
			scanf("%d", &a);
			Addedge(s, Get(i, j, 1), a);
			Addedge(Get(i, j, 1), Get(i, j, 0), INF);
			ans += a;
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1, a; j <= m; j++) {
			scanf("%d", &a);
			Addedge(Get(i, j, 2), t, a);
			ans += a;
			for (int k = 0; k < 5; k++) {
				int ni = i + addx[k];
				int nj = j + addy[k];
				if (ni < 1 || ni > n || nj < 1 || nj > m) continue;
				Addedge(Get(ni, nj, 0), Get(i, j, 2), INF);
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1, a; j <= m; j++) {
			scanf("%d", &a);
			ans += a;
			Addedge(s, Get(i, j, 3), a);
			for (int k = 0; k < 5; k++) {
				int ni = i + addx[k];
				int nj = j + addy[k];
				if (ni < 1 || ni > n || nj < 1 || nj > m) continue;
				Addedge(Get(i, j, 3), Get(ni, nj, 1), INF);
			}
		}
	}
	printf("%d", ans - Dinic());
	return 0;
}
posted @ 2021-12-09 22:17  Last_Breath  阅读(33)  评论(0编辑  收藏  举报