hdu 3996

最大权闭合图,建议和我一样不知道这个模型的acmer搜一下——20077.型》。下面我就谈一下我对最大权闭合图的理解。知道了最大权闭合图的模型后,此题可看做是求最大权闭合图。首先,原图是一些有着权值的点,那么目的就是在这些点中选择一些点,但选一个点i,必须选择它的前提条件的所有点,那么怎么选才能符合这一限制条件呢。那么在点i和i的前提条件的所有点之间连接一条有向边表示有联系。那么现阶段的问题就转换成了在刚建好的图中选择闭合子图,这一闭合图中的点就是要选择的点。然后因为要求权值最大,最终转换成了最大权闭合子图。然后就是最大权闭合子图怎么求。注意到最后所有的点就只有两种可能,选或者没有选。发现这和网络流中的割有联系,因为一个割就是把网络流中的点分成了两类。增加源点s和汇点t,在s和正权值的点之间连一条有向边,容量为权值。在负权值和t之间为一条边容量为权值的相反数。这样得到的网络流的一个割中不含有点集中的联系边的割就是一个选择(选出来的点构成闭合子图)方案,其中s集合是选择了的点,t集合中是没选的点。现在使联系两点的边的权值为无穷大。那么此网络流得到的最小割的s集合就是最大权闭合子图选定的点集。首先,联系两点的边注意是有向的)肯定不会在最小割中,就意味着只要i点选了,那么它的前提点也一定选了,反之却不一定,满足了闭合着一性质。然后,最大权就是正权之和-最小割容量。先认为所有正权点都被选了。对于正权点,s到他的边如果是是割,说明他没被选,那么得减去这条边的容量。对于负全点,如果到t的边是割,那么说明他被选了,所以因为他是负权,所以得减去他到t的容量。最后得到的就是结果。

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int maxn=2600;
const int inf=200000000;
const long long inf2=0x3f3f3f3f3f3f3f3fLL;
struct Edge
{
	int from,to,next;
	long long flow,cap;
};
Edge e[maxn*60*2];
int head[maxn],map[110][30],cur[maxn],d[maxn],rel[maxn][55][2],gap[maxn],p[maxn];
long long cost[maxn];
int	tot,num,s,t;
long long totg;
void addedge(int from,int to,long long cap)
{
	e[tot].from=from;e[tot].to=to;e[tot].flow=0;e[tot].cap=cap;
	e[tot].next=head[from];head[from]=tot;tot++;
	e[tot].from=to;e[tot].to=from;e[tot].flow=0;e[tot].cap=0;
	e[tot].next=head[to];head[to]=tot;tot++;
}
long long  augment()
{
	int x=t;
	long long a=inf2;
	while(x!=s)
	{
		a=min(a,e[p[x]].cap-e[p[x]].flow);
		x=e[p[x]].from;
	}
	x=t;
	while(x!=s)
	{
		e[p[x]].flow+=a;
		e[p[x]^1].flow-=a;
		x=e[p[x]].from;
	}
	return a;
}
long long maxflow()
{
	long long flow=0;
	memset(gap,0,sizeof(gap));
	memset(d,0,sizeof(d));
	gap[0]=t+1;
	int x=s;
	memcpy(cur,head,(t+1)*sizeof(int));
	while(d[s]<=t+1-1)
	{
		if(x==t)
		{
			flow+=augment();
			x=s;
		}
		int ok=0;
		for(int i=cur[x];i!=-1;i=e[i].next)
		{
			if(e[i].cap>e[i].flow&&d[x]==d[e[i].to]+1)
			{
				p[e[i].to]=i;
				ok=1;
				cur[x]=i;
				x=e[i].to;
				break;
			}
		}
		if(!ok)
		{
			int m=t+1-1;
			for(int i=head[x];i!=-1;i=e[i].next)
			{
				if(e[i].cap>e[i].flow) m=min(m,d[e[i].to]);
			}
			if(--gap[d[x]]==0) break;
			gap[d[x]=m+1]++;
			cur[x]=head[x];
			if(x!=s) x=e[p[x]].from;
		}
	}
	return flow;
}
int main()
{
	int amount,whe=0;
	cin>>amount;
	while(amount--)
	{
		whe++;
		memset(head,-1,sizeof(head));
		memset(rel,-1,sizeof(rel));
		tot=0;num=1;s=0;totg=0;
		int x,i,j,nlay,cos,val,fir,loc,ran,goldn,k;
		scanf("%d",&nlay);
		for(i=1;i<=nlay;i++)
		{
			scanf("%d",&goldn);
			for(j=1;j<=goldn;j++,num++)
			{
				map[i][j]=num;
				scanf("%d%d%d",&cos,&val,&fir);
				cost[num]=val-cos;
				if((val-cos)>0) totg+=(val-cos);
				for(k=0;k<fir;k++)
				{
					scanf("%d%d",&loc,&ran);
					rel[num][k][0]=loc;rel[num][k][1]=ran;
				}
			}
		}
		t=num;
		int tl,tr;
		for(i=1;i<=t-1;i++)
		{
			if(cost[i]>0) addedge(s,i,cost[i]);
			else addedge(i,t,-cost[i]);
			k=0;
			while(rel[i][k][0]!=-1)
			{
				addedge(i,map[rel[i][k][0]][rel[i][k][1]],inf2);
				k++;
			}
		}
		printf("Case #%d: %I64d\n",whe,totg-maxflow());
	}
	return 0;
}


转载于:https://www.cnblogs.com/lj030/archive/2012/12/12/3002287.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值