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
#include
#include
#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 (head3) 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>1));
	return 0;
}


by lych

2016.3.11

你可能感兴趣的:(bzoj)