[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;
}