[Luogu P4313] 文理分科

题目链接

题意简述

\(n*m\) 个同学分科,文理科会得到不同满意值,同时如果周围同学均选择相同分科,也会得到不同满意值,求最大满意值。

题目分析

一看到题就透着一股浓浓的最小割那味儿。

按照题意,将源点看作文科, 汇点看作理科

  • 先将每位同学单独选择的满意值进行加边操作,注意方向:
add(s,pos(i,j),u);
add(pos(i,j),t,u);

然后考虑周围同学的影响,先分析文科,下文将增加的满意值表示为 \(u\)

因为是四个同学共同对结果造成影响,又要保证不会影响前面的结果,所以考虑拆点:

  • 从源点(选择文科)向拆点连有向边,边权为 \(u\)(保证不会影响前面);

  • 从拆点向源点连有向边,边权为 \(u\) (因为满意值是增加的,所以要反向建边,最后结果才有可能更大);

  • 从周围四个点向拆点连有向边,边权为正无穷 (因为不确定大小,且后面的边会限制流量大小使其满足题意,所以直接来极大值)

add(s,cnt,u), add(cnt,pos(i,j),u);

add(cnt,pos(tx,ty),inf);

那么显而易见的是,理科(汇点)的连边操作因为结点顺序,方向与文科相反。

add(cnt,t,u), add(pos(i,j),cnt,u);

add(pos(tx,ty),cnt,inf);

最后,就是模板求最小割啦。

完整代码

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define rt register int
#define int long long
const int N = 3e4 + 10,M = 1e6 + 10, inf = 1e10;
struct node {
	int to,nex;
}e[M];
int n,m,s,t,top,ed,sum,cnt,dep[N],head[N],cur[N],tot = 1,f[M],q[N],a[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
inline void add(int x,int y,int w) {
	e[++tot] = (node) {y,head[x]}, f[tot] = w, head[x] = tot;
	e[++tot] = (node) {x,head[y]}, head[y] = tot; 
}
inline bool bfs() {
	memset(dep,-1,sizeof(dep));
	dep[s] = 0, cur[s] = head[s], q[top = 1] = s; ed = 1;
	int now,ver;
	while(top <= ed) {
		now = q[top++];
		for(rt i = head[now]; i; i = e[i].nex) {
			ver = e[i].to;
			if(dep[ver] == -1 && f[i]) {
				dep[ver] = dep[now] + 1, cur[ver] = head[ver];
				if(ver == t) return 1;
				q[++ed] = ver;
			}
		}
	}
	return 0;
}
inline int find(int x,int limit) {
	if(x == t) return limit;
	int flow = 0, tmp, ver;
	for(rt i = head[x]; i && flow < limit; i = e[i].nex) {
		ver = e[i].to;
		if(dep[ver] == dep[x] + 1 && f[i]) {
			tmp = find(ver,min(limit - flow,f[i]));
			if(!tmp) dep[ver] = -1;
			f[i] -= tmp, f[i ^ 1] += tmp, flow += tmp; 
		}
	}
	return flow;
}
inline int dinic() {
	int flow;
	while(bfs()) sum -= find(s,inf);
	return sum;
}
inline void read(int &x) {
	x = 0;int ff = 1;
	char s = getchar();
	while(s < '0' || s > '9') {
		if(s == '-') ff = -1;
		s = getchar();
	}
	while(s <= '9' && s >= '0') {x = x * 10 + s - '0', s = getchar(); }
	x *= ff;
}
inline int pos(int x,int y) {
	return m * (x - 1) + y;
}
signed main() {
	read(n), read(m);
	s = n * m + 1,t = s + 1, cnt = t;
	int u,tx,ty;
	for(rt i = 1; i <= n;  i++) {
		for(rt j = 1; j <= m; j ++) { 
			read(u), sum += u; 
			add(s,pos(i,j),u);
		}
	}
	for(rt i = 1; i <= n;  i++) {
		for(rt j = 1; j <= m; j ++) { 
			read(u), sum += u;
			add(pos(i,j),t,u);
		}
	}
	for(rt i = 1; i <= n; i ++) {
		for(rt j = 1; j <= m; j ++) {
			read(u), sum += u, cnt++;
			add(s,cnt,u), add(cnt,pos(i,j),u);
			for(rt k = 0; k < 4; k ++) {
				tx = i + a[k][0], ty = j + a[k][1];
				if(tx < 1 || ty < 1 || tx > n || ty > m) continue;
				add(cnt,pos(tx,ty),inf);
			}
		}
	}
	for(rt i = 1; i <= n; i ++) {
		for(rt j = 1; j <= m; j ++) {
			read(u), sum += u, cnt++;
			add(cnt,t,u), add(pos(i,j),cnt,u);
			for(rt k = 0; k < 4; k ++) {
				tx = i + a[k][0], ty = j + a[k][1];
				if(tx < 1 || ty < 1 || tx > n || ty > m) continue;
				add(pos(tx,ty),cnt,inf);
			}
		}
	}
	printf("%d",dinic());
	return 0;
} 
posted @ 2021-01-16 16:28  Spring-Araki  阅读(74)  评论(0编辑  收藏  举报