bzoj2127happiness(最小割)
一眼最小割。
一种比较好想的建图方式如下:
连源点表示学文,连汇点表示学理,然后adde(S,id(i,j),a[i][j]),adde(id(i,j),T,b[i][j]);对于相邻座位选择同一科的情况,建立新节点,然后若学文,则新点向T连一条流量为价值的边,然后两名同学向该点连接流量为inf的边。学理也是类似的。
#include<cstdio> #include<algorithm> #include<cstring> #define FOR(x,y) for(int i=1;i<=x;i++)for(int j=1;j<=y;j++) const int N=50100,M=3e5+7; using namespace std; int n,m,T,cnt,tot,ans,hd[N],lv[N],q[N],v[M],nxt[M],w[M]; void adde(int x,int y,int z) { v[++cnt]=y,w[cnt]=z,nxt[cnt]=hd[x],hd[x]=cnt; v[++cnt]=x,w[cnt]=0,nxt[cnt]=hd[y],hd[y]=cnt; } bool bfs() { int qs=0,qe=1; memset(q,0,sizeof q); memset(lv,0,sizeof lv); q[0]=0,lv[0]=1; while(qs<qe) { int u=q[qs++]; if(u==T)break; for(int i=hd[u];i;i=nxt[i]) if(w[i]&&!lv[v[i]]){lv[v[i]]=lv[u]+1;q[qe++]=v[i];} } if(lv[T])return 1;return 0; } int dfs(int u,int low) { if(u==T||!low)return low; int sum=0; for(int i=hd[u];i;i=nxt[i]) if(w[i]&&lv[v[i]]==lv[u]+1) { int tmp=dfs(v[i],min(w[i],low)); sum+=tmp,low-=tmp,w[i]-=tmp,w[i^1]+=tmp; if(!low)return sum; } if(low)lv[u]=-1; return sum; } int id(int x,int y){return(x-1)*m+y;} int main() { scanf("%d%d",&n,&m); T=5*n*m+1,cnt=1,tot=n*m; int x; FOR(n,m)scanf("%d",&x),adde(0,id(i,j),x),ans+=x; FOR(n,m)scanf("%d",&x),adde(id(i,j),T,x),ans+=x; FOR(n-1,m) scanf("%d",&x),tot++,adde(0,tot,x),adde(tot,id(i,j),1e9),adde(tot,id(i+1,j),1e9),ans+=x; FOR(n-1,m) scanf("%d",&x),tot++,adde(tot,T,x),adde(id(i,j),tot,1e9),adde(id(i+1,j),tot,1e9),ans+=x; FOR(n,m-1) scanf("%d",&x),tot++,adde(0,tot,x),adde(tot,id(i,j),1e9),adde(tot,id(i,j+1),1e9),ans+=x; FOR(n,m-1) scanf("%d",&x),tot++,adde(tot,T,x),adde(id(i,j),tot,1e9),adde(id(i,j+1),tot,1e9),ans+=x; while(bfs())ans-=dfs(0,1e9); printf("%d",ans); }