happiness bzoj2127 洛谷1646 最小割

题意:

有n行m列,每个位置有一个人,每个人有一个选文科的喜悦值和一个选理科的喜悦值。另外,如果前后左右相邻的两个人同选了文科或者同选了理科,会获得一个额外喜悦值。现在要求最大能获得的喜悦值。

这题似乎有人称之为文理分科?

一个人当然只能从文科和理科中选一个,就相当于一部分人选了文科,一部分人选了理科,变成了一个集合划分的问题。

我听说了好几种建边的方法,我只说一下自己用的方法,因为别的方法都没有仔细考虑过。

感觉这种把一些事物划分到两个集合的题经常是最小割,这题就是。

本题先求出所有喜悦值之和,再用最小割求出最小喜悦值损失。

首先我们考虑一个人要么选文科,要么选理科,所以我们对于每个人,从源点向这个人连选文科的喜悦值,并从这个人向汇点连选理科的喜悦值,这样文理一定会割掉一条。

再考虑相邻带来的额外喜悦值。我这里采用了新建一些节点的方法,对于每相邻的两个人,新建出两个新点,从源点向第一个新点连同选文科的喜悦值,然后从这个新点向这相邻的两个人分别连流量正无穷的边(不可割掉),再从这两个相邻的人分别向第二个新点连流量为正无穷的边,最后从第二个新点向汇点连流量为同选理科的额外喜悦值的边。

这样就建好图了,跑完最小割之后用喜悦值总和减去最小割就是答案。

如果不理解的话可以画个图看看,样例数据的图就不错,你会发现这样割出来的一定是对的。

这样建出来的图,对于任意两个相邻的人,要么把选理科和同选理科的喜悦割掉,要么把选文科和同选文科的喜悦割掉,要么割掉一人的文、另一人的理和两人同文同理的喜悦。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,st,ed,hed[500001],cnt,ji;
int res,dep[50009],h,t,q[50009],sum;//dep有点大,跑得比较慢
struct node
{
	int to,next,c;
}a[5000001];
void add(int from,int to,int c)
{
	a[++cnt].to=to;
	a[cnt].c=c;
	a[cnt].next=hed[from];
	hed[from]=cnt;
	a[++cnt].to=from;
	a[cnt].c=0;
	a[cnt].next=hed[to];
	hed[to]=cnt;
}
int bfs()
{
	memset(dep,0,sizeof(dep));
	q[1]=st;
	h=1;t=2;
	dep[st]=1;
	while(h!=t)
	{
		int x=q[h];
		for(int i=hed[x];i;i=a[i].next)
		{
			int y=a[i].to;
			if(a[i].c&&dep[y]==0)
			{
				dep[y]=dep[x]+1;
				q[t++]=y;
			}
		}
		h++;
	}
	if(dep[ed]>0)
	return 1;
	else
	return 0;
}
int flow(int x,int f)
{
	if(x==ed)
	return f;
	int s=0,t;
	for(int i=hed[x];i;i=a[i].next)
	{
		int y=a[i].to;
		if(a[i].c&&dep[y]==dep[x]+1&&s<f)
		{
			s+=(t=flow(y,min(a[i].c,f-s)));
			a[i].c-=t;
			a[i^1].c+=t;
		}
	}
	if(s==0)
	dep[x]=0;
	return s;
}
int main()
{
	scanf("%d%d",&n,&m);
	cnt=1;
	st=50000,ed=50001;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int x;
			scanf("%d",&x);
			add(st,(i-1)*m+j,x);
			sum+=x;
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int x;
			scanf("%d",&x);
			add((i-1)*m+j,ed,x);
			sum+=x;
		}
	}
	ji=n*m;
	for(int i=1;i<n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int x;
			scanf("%d",&x);
			add(st,++ji,x);
			add(ji,(i-1)*m+j,2e9);
			add(ji,i*m+j,2e9);
			sum+=x;
		}
	}
	for(int i=1;i<n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int x;
			scanf("%d",&x);
			add(++ji,ed,x);
			add((i-1)*m+j,ji,2e9);
			add(i*m+j,ji,2e9);
			sum+=x;
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<m;j++)
		{
			int x;
			scanf("%d",&x);
			add(st,++ji,x);
			add(ji,(i-1)*m+j,2e9);
			add(ji,(i-1)*m+j+1,2e9);
			sum+=x;
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<m;j++)
		{
			int x;
			scanf("%d",&x);
			add(++ji,ed,x);
			add((i-1)*m+j,ji,2e9);
			add((i-1)*m+j+1,ji,2e9);
			sum+=x;
		}
	}
	while(bfs())
	res+=flow(st,2e9);
	printf("%d\n",sum-res);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/80011110