[题解] 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;
}