bzoj3894: 文理分科
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3894
思路:令S为学文,T为学理
对于额外的收入,新加一个点
也可以用二元组建图来解决
还是这个图,x表示一个学生,与S连通表示学文,与T连通表示学理
y表示新加的点,用来计算同时学理的额外收益,与S连通表示不要额外收益,与T连通表示要额外收益
先算出总收益,在用最小割减去即可
解方程:
c+d=B[i]+WB//学生i学文,不要额外收益,损失学理收益,损失额外收益
a+b=A[i]//学生i学理,要额外收益,损失学文收益
b+e+c=inf//学生i学文,要额外收益,这不可能,损失为inf
a+f+d=A[i]+WB//学生i学理,不要额外收益,损失学文收益,损失额外收益
a=A[i]
b=0
c=B[i]
d=WB
e=inf
f=0
学文类似,但要注意一些边方向不同,收益也不同
所以最终建图就是:
S向每个学生连边,容量为学文的收益,每个学生向T连边,容量为学理的收益
S向每个代表相邻5个人都学文收益点连边,容量为同时学文的收益
该点再向对应5个人连边,容量为inf,表示只要有人学理,就必须割掉同时学文的收益
学理类似
对应5个人向该点连inf的边,该点向T连同时学理的收益的边
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int maxn=30010,maxm=1000010,inf=1061109567; const int dx[]={0,0,0,1,-1}; const int dy[]={0,1,-1,0,0}; using namespace std; int n,m,sum; int stu(int x,int y){return (x-1)*m+y;} int art(int x,int y){return (x-1)*m+y+n*m;} int sci(int x,int y){return (x-1)*m+y+n*m*2;} struct Flow{ int pre[maxm],now[maxn],son[maxm],val[maxm],tot,dis[maxn],q[maxn+10],head,tail,S,T; void add(int a,int b,int c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;} void ins(int a,int b,int c){add(a,b,c),add(b,a,0);} void init(){memset(now,0,sizeof(now)),tot=1,S=0,T=3*n*m+1;} void build(){ for (int i=1,x;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&x),sum+=x,ins(S,stu(i,j),x); for (int i=1,x;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&x),sum+=x,ins(stu(i,j),T,x); for (int i=1,x;i<=n;i++) for (int j=1;j<=m;j++){ scanf("%d",&x),sum+=x; for (int k=0;k<5;k++){ int nx=i+dx[k],ny=j+dy[k]; if (nx<1||nx>n||ny<1||ny>m) continue; ins(art(i,j),stu(nx,ny),inf); } ins(S,art(i,j),x); } for (int i=1,x;i<=n;i++) for (int j=1;j<=m;j++){ scanf("%d",&x),sum+=x; for (int k=0;k<5;k++){ int nx=i+dx[k],ny=j+dy[k]; if (nx<1||nx>n||ny<1||ny>m) continue; ins(stu(nx,ny),sci(i,j),inf); } ins(sci(i,j),T,x); } } bool bfs(){ memset(dis,-1,sizeof(dis)); q[tail=1]=S,dis[S]=head=0; while (head!=tail){ if (++head>maxn) head=1; int x=q[head]; for (int y=now[x];y;y=pre[y]) if (val[y]&&dis[son[y]]==-1){ if (++tail>maxn) tail=1; dis[son[y]]=dis[x]+1,q[tail]=son[y]; } } return dis[T]>0; } int find(int x,int low){ if (x==T) return low; int y,res=0; for (y=now[x];y;y=pre[y]){ if (dis[son[y]]!=dis[x]+1||!val[y]) continue; int tmp=find(son[y],min(low,val[y])); res+=tmp,low-=tmp,val[y]-=tmp,val[y^1]+=tmp; if (!low) break; } if (!y) dis[x]=-1; return res; } void work(){ while (bfs()) sum-=find(S,inf); printf("%d\n",sum); } }F; int main(){ scanf("%d%d",&n,&m),F.init(); F.build(),F.work(); return 0; }