BZOJ3894:文理分科(最大流)(同BZoj3438)
文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠 结过) 小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想知道,大家应该如何选择,才能使所有人的满意值之和最大。请 告诉他这个最大值。 Input 第一行为两个正整数:n,m 接下来n术m个整数,表示art[i][j]; 接下来n术m个整数.表示science[i][j]; 接下来n术m个整数,表示same_art[i][j]; Output 输出为一个整数,表示最大的满意值之和 Sample Input 3 4 13 2 4 13 7 13 8 12 18 17 0 5 8 13 15 4 11 3 8 11 11 18 6 5 1 2 3 4 4 2 3 2 3 1 0 4 3 2 3 2 0 2 2 1 0 2 4 4 Sample Output
152
题意:给定一个N*M的矩阵,每个格子的人可以学文或者学理,学文和学理各有一个满意度,如果以某人为中心的十字内所有人都学文或者学理都会得到一个额外满意度,求最大满意度之和。
思路:BZOJ上面有很多此类题型,用网络里解决的方案大概有两种。
解法1:直接建图求最大流,源点S为学理,汇点T为学文。然后对于学理的“组合”,加一个点W:由源点S加边指向W,容量为组合的收益;W加边指向组合的成员,容量为inf。 同样的,对于学文的“组合”,加一个点E:由E加边指向汇点T,容量为组合的收益;组合的成员加边指向E,容量为inf。 然后求出最小割。
有:ans=sum-maxflow
解法2:先假设全部学理科,再把边权做差(学理-学文的净收益),再求最大闭合权子图:原点与代价点连接,收益点与汇点连接; 收益和-最大流=最大净收益。 那么现在的基本代价或者收益是ai-bi,。然后破坏集合的代价是c1i,得到集合的收益是c2i。 差不多就酱紫。具体的请去看popoqqq的题解。
体会:解法2更有道理一些,但是解法1更通用,无论“有格外收益的组合”长什么样子,都可以轻松加边,然后求最大流。
解法1的代码:
#include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; const int maxn=2000010; const int inf=1000000000; int N,M,S,T,ans,maxflow; int Laxt[maxn],To[maxn],Next[maxn],cap[maxn],cnt=1; int vd[maxn],dis[maxn]; int xx[5]={0,0,0,1,-1}; int yy[5]={0,1,-1,0,0}; void add(int u,int v,int c) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; cap[cnt]=c; } int id(int u,int v){ return (u-1)*M+v; } int sap(int u,int flow) { if(flow==0||u==T) return flow; int tmp,delta=0; for(int i=Laxt[u];i;i=Next[i]){ if(dis[u]==dis[To[i]]+1&&cap[i]>0){ tmp=sap(To[i],min(cap[i],flow-delta)); delta+=tmp; cap[i]-=tmp; cap[i^1]+=tmp; if(delta==flow||dis[S]>T+1) return delta; } } vd[dis[u]]--; if(vd[dis[u]]==0) dis[S]=T+2; vd[++dis[u]]++; return delta; } int main() { int i,j,k,x; scanf("%d%d",&N,&M); S=0; T=3*N*M+1; for(i=1;i<=N;i++) for(j=1;j<=M;j++){ scanf("%d",&x); ans+=x; add(S,id(i,j),x); add(id(i,j),S,0); } for(i=1;i<=N;i++) for(j=1;j<=M;j++){ scanf("%d",&x); ans+=x; add(id(i,j),T,x); add(T,id(i,j),0); } for(i=1;i<=N;i++) for(j=1;j<=M;j++){ scanf("%d",&x); ans+=x; add(S,N*M+id(i,j),x); add(N*M+id(i,j),S,0); for(k=0;k<=4;k++){ int x=i+xx[k],y=j+yy[k]; if(x>=1&&x<=N&&y>=1&&y<=M){ add(N*M+id(i,j),id(x,y),inf); add(id(x,y),N*M+id(i,j),0); } } } for(i=1;i<=N;i++) for(j=1;j<=M;j++){ scanf("%d",&x); ans+=x; add(2*N*M+id(i,j),T,x); add(T,2*N*M+id(i,j),0); for(k=0;k<=4;k++){ int x=i+xx[k],y=j+yy[k]; if(x>=1&&x<=N&&y>=1&&y<=M){ add(id(x,y),2*N*M+id(i,j),inf); add(2*N*M+id(i,j),id(x,y),0); } } } while(dis[S]<=T) maxflow+=sap(S,inf); printf("%d\n",ans-maxflow); return 0; }
It is your time to fight!