最大权闭合子图

本文介绍如何通过构造网络流图并利用最小割原理来寻找图中的最大权闭合子图,包括定义、求解方法及证明,并提供了一个具体的编程实现案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考资料:胡伯涛《最小割模型在信息学竞赛中的应用》

1.什么是最大权闭合子图?

如图所示(图是网上找的)

如果对于一个点集合,其中任何一个点都不能到达此集合以外的点,这就叫做闭合子图。每个点都有一个权值,那么最大权闭合子图就是权值最大的那个闭合子图。

图中的闭合子图有空集,{5},{2,5},{4,5},{2,4,5},{3,4,5},{1,2,4,5},{1,2,3,4,5}

其中最大权闭合子图是{3,4,5}

2.怎么求最大权闭合子图?

首先构造一张网络流图,把每个正权值的点i连接(s,i,x)(x是权值)。把每个负权值的点i连接(i,t,-x),原图中的边连接(i,j,正无穷)然后求最小割,所有正权值之和减去最小割就得到了最大权闭合子图。

3.为什么?(以下证明较为晦涩难懂)

我这个蒟蒻看同一段话看了两个小时才勉强明白。

定义简单割:就是割边都与源点或汇点连接的割。

证明1:最小割一定是简单割

这个证明很简单吧......因为如果是一条不和源点与汇点连通的点的话,那么这条边的容量就是正无穷,那么就不可能是最小割

证明2:简单割和闭合子图存在一一对应关系(表示的东西:一个简单割表示不在闭合子图的正权点和在此闭合子图里的负权点)

这个证明有点绕......详细的还是看pdf吧....

证明闭合图是简单割:如果闭合图不是简单割,那么有一条容量为正无穷的边,说明闭合图中有一条出边不在闭合图中,不符合闭合图的定义

证明简单割是闭合图:简单割里没有容量为正无穷的边,所以假如S(起点那一块)中的点s可以到达一点t,那么如果t是正权,则在S中,如果t是负权,则不在T中(终点那一块)

证明3:取最小割的时候得到的是最大权

那么w(闭合图)=闭合图中正权-(-闭合图中负权)

回忆一下简单割的表示的东西,可以得到:

w(闭合图)+割=闭合图中正权-(-闭合图中负权)+非闭合图正权+(-闭合图中正权)

那么w(闭合图)+割=闭合图中负权+非闭合图中正权

w(闭合图)=正权点之和-割

所以要让闭合图最大,就要让割最小

4.应用

hoj2634就是这样的题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<vector>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
int m,n,T,sum,s,t;
long long flow[20005];
int go[200005],h[205],ne[200005],level[205],que[205];
long long inf=1999999999,he;
void add(int x,int y,long long z){
	sum++;go[sum]=y;ne[sum]=h[x];h[x]=sum;flow[sum]=z;
	sum++;go[sum]=x;ne[sum]=h[y];h[y]=sum;flow[sum]=0;
}
bool bfs(){
	int i,head=1,tail=1,from;
	for(i=s;i<=t;i++)level[i]=0;
	level[s]=1;que[1]=s;
	while(head<=tail){
		from=que[head];
		if(from==t)return 1;
		for(i=h[from];i!=-1;i=ne[i])
			if(level[go[i]]==0&&flow[i]>0){
				level[go[i]]=level[from]+1;
				tail++;que[tail]=go[i];
			}
		head++;
	}
	return 0;
}
long long dfs(int x,long long liu){
	int i;
	long long sum=0,kl;
	if(x==t)return liu;
	for(i=h[x];i!=-1;i=ne[i])
		if(level[go[i]]==level[x]+1&&flow[i]>0){
			kl=dfs(go[i],min(liu-sum,flow[i]));
			sum+=kl;flow[i]-=kl;flow[i^1]+=kl;
			if(sum==liu)return sum;
		}
	return sum;
}
long long find(){
	long long ans=0;
	while(bfs())ans+=dfs(s,inf);
	return ans;
}
int main()
{
   	int i,j,num,y;
   	long long x;
   	scanf("%d",&T);
   	while(T){
   		T--;sum=1;he=0;
   		scanf("%d%d",&n,&m);
   		s=0;t=n+m+1;
   		for(i=s;i<=t;i++)h[i]=-1;
   		for(i=1;i<=n;i++){scanf("%lld",&x);add(s,i,x);he+=x;}
   		for(i=1;i<=m;i++){scanf("%lld",&x);add(i+n,t,x);}
   		for(i=1;i<=n;i++){
   			scanf("%d",&num);
   			for(j=1;j<=num;j++){scanf("%d",&y);add(i,y+n+1,inf);}
   		}
   		printf("%lld\n",he-find());
   	}
   	return 0;
} 
/*项目:1~n,人:n~m+n*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值