$\newcommand{\edge}[1]{\mathop\rightarrow\limits^{#1}}$有一些求最大权值的问题可以先全部选择,再考虑用尽可能小的代价删除某些不合法的选择,转化为最小割
有一个来自这篇博客的极妙的建图的方法:
考虑两个人$x,y$,假设$a_i$表示$i$选文科的贡献,$b_i$表示$i$选理科的贡献,$c_{i,j}$表示$i,j$都选文科的贡献,$d_{i,j}$表示$i,j$都选理科的贡献,我们这样建图
答案是输入中的所有贡献之和减去最小割
对于$S\edge{a_x}x\edge{b_x}T$这样的路径,有且仅有一条边会被割掉,对应着选另一科
再考虑两个人$x,y$,如果割掉$a_x,a_y$,那么剩下的图中最小割只能是$c_{x,y}$,恰好对应两人都选理的情况
如果割掉$b_x,b_y$,那么剩下的图中最小割只能是$d_{x,y}$,恰好对应两人都选文的情况
如果割掉$a_x,b_y$或$a_y,b_x$,那么$c_{x,y},d_{x,y}$都会被割掉,对应着两人分选两科的情况
不得不说建图是非常巧妙的,感觉自己没脑子来想这么牛逼的建图==
#include<stdio.h> #include<string.h> const int inf=2147483647; int min(int a,int b){return a<b?a:b;} int h[50010],cur[50010],nex[300010],to[300010],cap[300010],dis[50010],q[300010],M=1,S,T; void add(int a,int b,int c){ M++; to[M]=b; cap[M]=c; nex[M]=h[a]; h[a]=M; M++; to[M]=a; cap[M]=0; nex[M]=h[b]; h[b]=M; } bool bfs(){ int head,tail,x,i; memset(dis,-1,sizeof(dis)); head=tail=1; q[1]=S; dis[S]=0; while(head<=tail){ x=q[head]; head++; for(i=h[x];i;i=nex[i]){ if(cap[i]&&dis[to[i]]==-1){ dis[to[i]]=dis[x]+1; if(to[i]==T)return 1; tail++; q[tail]=to[i]; } } } return 0; } int dfs(int x,int flow){ if(x==T)return flow; int i,f; for(i=cur[x];i;i=nex[i]){ if(cap[i]&&dis[to[i]]==dis[x]+1){ f=dfs(to[i],min(flow,cap[i])); if(f){ cap[i]-=f; cap[i^1]+=f; if(cap[i])cur[x]=i; return f; } } } dis[x]=-1; return 0; } int dicnic(){ int ans=0,tmp; while(bfs()){ memcpy(cur,h,sizeof(h)); while(tmp=dfs(S,inf))ans+=tmp; } return ans; } int n,m; int tr(int x,int y){return(x-1)*m+y;} int main(){ int i,j,x,t,s; scanf("%d%d",&n,&m); S=n*m+1; T=n*m+2; t=n*m+2; s=0; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ scanf("%d",&x); s+=x; add(S,tr(i,j),x); } } for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ scanf("%d",&x); s+=x; add(tr(i,j),T,x); } } for(i=1;i<n;i++){ for(j=1;j<=m;j++){ scanf("%d",&x); s+=x; t++; add(S,t,x); add(t,tr(i,j),x); add(t,tr(i+1,j),x); } } for(i=1;i<n;i++){ for(j=1;j<=m;j++){ scanf("%d",&x); s+=x; t++; add(tr(i,j),t,x); add(tr(i+1,j),t,x); add(t,T,x); } } for(i=1;i<=n;i++){ for(j=1;j<m;j++){ scanf("%d",&x); s+=x; t++; add(S,t,x); add(t,tr(i,j),x); add(t,tr(i,j+1),x); } } for(i=1;i<=n;i++){ for(j=1;j<m;j++){ scanf("%d",&x); s+=x; t++; add(tr(i,j),t,x); add(tr(i,j+1),t,x); add(t,T,x); } } printf("%d",s-dicnic()); }