zoukankan      html  css  js  c++  java
  • [题解] 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;
    }
    
  • 相关阅读:
    关于JDK中的设计模式
    关于Java中的构造方法和set方法()给属性赋值
    关于Object[]数组强转成Integer[]类型的数组.
    [ImportNew]Java中的Timer类和TimerTask类
    关于Linux最基本总结
    关于数组
    关于Linux系统和Windows系统中文件夹的命名规范
    关于数组集合之间的转换
    关于Java获取系统信息
    关于Windows常用命令
  • 原文地址:https://www.cnblogs.com/C202202chenkelin/p/15669567.html
Copyright © 2011-2022 走看看