[BZOJ 3894]文理分科(最小割)
Description
文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠结过)
小P所在的班级要进行文理分科。他的班级可以用一个n*m的矩阵进行
描述,每个格子代表一个同学的座位。每位同学必须从文科和理科中选择
一科。同学们在选择科目的时候会获得一个满意值。满意值按如下的方式
得到:
1.如果第i行第秒J的同学选择了文科,则他将获得art[i][j]的满意值,如
果选择理科,将得到science[i][j]的满意值。
2.如果第i行第J列的同学选择了文科,并且他相邻(两个格子相邻当且
仅当它们拥有一条相同的边)的同学全部选择了文科,则他会更开
心,所以会增加same_art[i][j]的满意值。
3.如果第i行第j列的同学选择了理科,并且他相邻的同学全部选择了理
科,则增加same_science[i][j]的满意值。
小P想知道,大家应该如何选择,才能使所有人的满意值之和最大。请
告诉他这个最大值。
Solution
妹主席在JZYZ讲过的一道,想起来了于是去切一下
“依然是把最大化收益转化为最小化得不到的收益。
对于每个点,向S和T分别连容量为ai,bi的边,那么V代表划分到A集合的点,V’代表c[s,V’]代表划分到B集合中的元素失去的ai收益,c[V,t]相反,因为选到A集合的点和选到B集合的点相互之间不会产生影响,所以c[V,V’]=0
对于每个附加条件我们额外建立一个点,假设是要求一些元素必须划分到A集合中,那么从源点向它连一条容量为ci的边,从它向要求的元素连一条容量为正无穷的边,这样,割掉源点连向它的边(舍弃掉这个收益)和割掉它连向的所有点连向汇点的边(舍弃掉这些点划分到bi的收益)必须选择其中一个。
点数为O(n+m),边数为O(n+m+sigma(|S|))”
——妹主席的教诲
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<queue> #define Max(a,b) (a>b?a:b) #define Min(a,b) (a<b?a:b) #define INF 0x3f3f3f3f using namespace std; int n,m,s,t,level[50005],head[50005],cnt=0,ans=0; int dx[5]={1,-1,0,0,0},dy[5]={0,0,1,-1,0}; struct Node{ int next,to,cap; }Edges[1000005]; int Read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ x=x*10+c-'0';c=getchar(); } return x*f; } void addedge(int u,int v,int c) { Edges[cnt].next=head[u]; head[u]=cnt; Edges[cnt].to=v; Edges[cnt++].cap=c; } void Insert(int u,int v,int c) { addedge(u,v,c); addedge(v,u,0); } int number(int i,int j) { return (i-1)*m+j; } bool bfs() { memset(level,-1,sizeof(level)); queue<int>q; q.push(s);level[s]=0; while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];~i;i=Edges[i].next) { int v=Edges[i].to; if(level[v]==-1&&Edges[i].cap) level[v]=level[u]+1,q.push(v); } } if(level[t]==-1)return false; return true; } int dfs(int u,int Maxflow) { if(u==t)return Maxflow; int flow=0,d; for(int i=head[u];~i&&Maxflow>flow;i=Edges[i].next) { int v=Edges[i].to; if(level[v]==level[u]+1&&Edges[i].cap) { d=dfs(v,Min(Maxflow-flow,Edges[i].cap)); Edges[i].cap-=d; Edges[i^1].cap+=d; flow+=d; } } if(!flow)level[u]=-1; return flow; } int Dinic() { int res=0,d; while(bfs()) { while(d=dfs(s,INF)) res+=d; } return res; } int main() { memset(head,-1,sizeof(head)); n=Read(),m=Read(); s=0,t=3*n*m+1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int art=Read(); ans+=art; Insert(s,number(i,j),art); } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int science=Read(); ans+=science; Insert(number(i,j),t,science); } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int same_art=Read(),p=number(i,j)+m*n; ans+=same_art; Insert(s,p,same_art); for(int k=0;k<5;k++) { if(i+dx[k]>0&&i+dx[k]<=n&&j+dy[k]>0&&j+dy[k]<=m) Insert(p,number(i+dx[k],j+dy[k]),INF); } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int same_science=Read(),p=number(i,j)+2*m*n; ans+=same_science; Insert(p,t,same_science); for(int k=0;k<5;k++) { if(i+dx[k]>0&&i+dx[k]<=n&&j+dy[k]>0&&j+dy[k]<=m) Insert(number(i+dx[k],j+dy[k]),p,INF); } } printf("%d\n",ans-Dinic()); return 0; }