[BZOJ2127]happiness

$\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());
}

猜你喜欢

转载自www.cnblogs.com/jefflyy/p/8906127.html