看到这种题目,一般就是想到构建网络流,然后一种割对应一种方案(本题中为割完后S和选文科的相连,T和选理科的项链),求最小割把。。
考虑一个点x(代表一个同学),如果选文科,就要和T割断(理科割S),割断的为损失的部分,即选理科的喜悦值,因此连边x->T,容量为选理科的喜悦值;同理连边S->x,容量为选文科的喜悦值。
然后考虑两个相邻的点x,y,不同的方案对答案产生的影响。设u为x,y都选文科的额外喜悦值;v为x,y都选理科的额外喜悦值,flow(p,q)表示从p到q连容量为flow(p,q)的边(不包括前面已经连好的边)。那么有三种情况:
1.x,y都选文科,那么需要将x和y到T的边的割断,各自损失的喜悦值已经减去了,那么还需要减去损失的都选理科的喜悦值。即flow(x,T)+flow(y,T)=v;
2.x,y都选理科,同上分析可得,flow(S,x)+flow(S,y)=u;
3.x,y一个选文科一个选理科,不妨设x选文科。那么显然都选文和都选理的额外喜悦值都损失了。这时我们发现前面两条已经把S->x,y和x,y->T的边都建好了,不能改动(否则上面就出问题了)。因此考虑在x->y或者y->x中连边。那么由于割完后S和x相连,y与T相连,那么如果有一条x->y的边,则必须要将这条边割断,因此在x->y的边上做文章。因此得到flow(S,x)+flow(x,y)+flow(y,T)=u+v;同理得到flow(S,y)+flow(y,x)+flow(x,T)=u+v。
然后就发现这需要flow(S,x)=flow(S,y)=u/2,flow(x,T)=flow(y,T)=v/2,flow(x,y)=flow(y,x)=(u+v)/2即可。那么这些边再加上前面的边就是完整的建图了。操作时把起点和终点相同的边合并可以加快速度。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #define N 100005 #define inf 1000000000 using namespace std; int n,m,tot=1,gol,a[6][105][105],fst[N],pnt[N],len[N],nxt[N],d[N],h[N]; int pt(int x,int y){ return (x-1)*n+y; } void add(int x,int y,int z){ pnt[++tot]=y; len[tot]=z; nxt[tot]=fst[x]; fst[x]=tot; } void ins(int x,int y,int z,int p){ add(x,y,z); add(y,x,z*p); } bool bfs(){ int head=0,tail=1; h[1]=0; memset(d,-1,sizeof(d)); d[0]=1; while (head<tail){ int x=h[++head],p; for (p=fst[x]; p; p=nxt[p]) if (len[p]){ int y=pnt[p]; if (d[y]==-1){ d[y]=d[x]+1; h[++tail]=y; } } } return d[gol]!=-1; } int dfs(int x,int rst){ if (x==gol || !rst) return rst; int p,flow=0; for (p=fst[x]; p; p=nxt[p]) if (len[p]){ int y=pnt[p]; if (d[x]+1!=d[y]) continue; int tmp=dfs(y,min(rst,len[p])); if (!tmp) continue; flow+=tmp; len[p]-=tmp; len[p^1]+=tmp; rst-=tmp; if (!tmp) break; } if (!flow) d[x]=-1; return flow; } int main(){ scanf("%d%d",&m,&n); int i,j,k,sum=0,ans=0; for (k=0; k<6; k++){ int x=m,y=n; if (k>3) y--; else if (k>1) x--; for (i=1; i<=x; i++) for (j=1; j<=y; j++){ scanf("%d",&a[k][i][j]); sum+=a[k][i][j]; } } gol=m*n+1; for (i=1; i<=m; i++) for (j=1; j<=n; j++){ ins(0,pt(i,j),(a[0][i][j]<<1)+a[2][i][j]+a[2][i-1][j]+a[4][i][j]+a[4][i][j-1],0); ins(pt(i,j),gol,(a[1][i][j]<<1)+a[3][i][j]+a[3][i-1][j]+a[5][i][j]+a[5][i][j-1],0); if (i<m) ins(pt(i,j),pt(i+1,j),a[2][i][j]+a[3][i][j],1); if (j<n) ins(pt(i,j),pt(i,j+1),a[4][i][j]+a[5][i][j],1); } while (bfs()) ans+=dfs(0,inf); printf("%d\n",sum-(ans>>1)); return 0; }
by lych
2016.3.11