bzoj2127 happiness 最小割

       看到这种题目,一般就是想到构建网络流,然后一种割对应一种方案(本题中为割完后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

你可能感兴趣的:(网络流,最小割,最大流)