【题解】文理分科

题意描述

在这里插入图片描述

sol :

考虑如何转化问题。假设我把所有的收益都加起来,减去最少的收益就是答案。

原问题 = 总收益 - 最小割

  1. 文理不能同时选

考虑对于每个点 i , s 向 i 连一条容量为 art[i] 的 有向边 , i 向 t 也连一条容量为 sci[i] 的边,这样 art[i] 和 sci[i] 必须断一条。

  1. 对于每个点,要考虑它和上下左右同文同理时的贡献

对于同文点,记作 i2 ,从 s 向 i2 连一条容量为 same_art[i] 的边,i2 再向包括它自己的相邻点连一条容量为 inf 的边。

对于同理点,记作 i3 ,从 i3 向 t 连一条容量为 same_sci[i] 的边,再从包括它自己的相邻边向它连一条容量为 inf 的边

在这里插入图片描述
我们来证明它为什么是正确的。

显然文理边至少会断一个,而对于一个点的同文或同理不能同时存在(否则存在一条流量大于 0 的路径)。对于文理边同时断掉的情况,如果断掉的是同文,就把理边恢复;如果断掉的是同理,就把文边恢复,此时同样是割。

证明完毕。

#include<bits/stdc++.h> #define ll long long #define INF 0x3f3f3f3f using namespace std; const int N=105*105*3; const int M=1e6+5; int n,m,s,t,rt,a[4][N],id[3][N],res; int lab[N],que[N],cur[N]; int tot,head[M],nxt[M],to[M],cap[M]; int dx[5]={-1,0,1,0},dy[5]={0,1,0,-1}; void add(int u,int v,int w) { tot++; nxt[tot]=head[u],head[u]=tot,to[tot]=v,cap[tot]=w; } bool BFS() { for(int i=0;i<=rt;i++) lab[i]=0; lab[t]=1; int qhead=0,qtail=0; que[qtail++]=t; while(qhead!=qtail) { int u=que[qhead++]; for(int k=head[u];k;k=nxt[k]) { int v=to[k]; if(cap[k^1]==0||lab[v]>0) { continue; } lab[v]=lab[u]+1; // if(v==s) return 1; que[qtail++]=v; } } return lab[s]!=0; } int flow(int u,int limit) { if(u==t) { return limit; } int used=0; for(int k=cur[u];k;k=nxt[k]) { int v=to[k]; cur[u]=k; if(lab[u]!=lab[v]+1||cap[k]==0) { continue; } int tmp=min(limit-used,cap[k]); int ret=flow(v,tmp); used+=ret; cap[k]-=ret; cap[k^1]+=ret; if(ret==0||cap[k]==0) { cur[u]=nxt[k]; } if(limit==used) break; } return used; } int has(int i,int j) { return (i-1)*m+j; } bool outside(int x,int y) { if(x<1||x>n||y<1||y>m) return 1; return 0; } int dinic() { int tot=0; while(BFS()) { for(int i=0;i<=rt;i++) cur[i]=head[i]; tot+=flow(s,INF); } return tot; } int main() { tot=1; for(int i=0;i<=rt;i++) { head[i]=0; } s=rt=0; scanf("%d%d",&n,&m); for(int k=0;k<4;k++) { for(int i=1;i<=n*m;i++) { scanf("%d",&a[k][i]); res+=a[k][i]; } } for(int k=0;k<3;k++) { for(int i=1;i<=n*m;i++) { id[k][i]=++rt; } } t=++rt; for(int i=1;i<=n*m;i++) { add(s,id[0][i],a[0][i]); add(id[0][i],s,0); add(id[0][i],t,a[1][i]); add(t,id[0][i],0); } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { int u=id[1][has(i,j)]; int f=has(i,j); add(s,u,a[2][f]); add(u,s,0); for(int k=0;k<5;k++) { int ti=i+dx[k],tj=j+dy[k]; if(outside(ti,tj)) continue; int v=id[0][has(ti,tj)]; add(u,v,INF); add(v,u,0); } u=id[2][has(i,j)]; add(u,t,a[3][f]); add(t,u,0); for(int k=0;k<5;k++) { int ti=i+dx[k],tj=j+dy[k]; if(outside(ti,tj)) continue; int v=id[0][has(ti,tj)]; add(v,u,INF); add(u,v,0); } } } printf("%d",res-dinic()); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530209.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(16)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示