首页 > 其他分享 >最大权闭合子图

最大权闭合子图

时间:2023-02-24 14:11:15浏览次数:51  
标签:right 最大 int text 边权 源点 子图 闭合 建边

前置知识:最大权闭合子图。

这是个什么东东呢,它是对于每一个点赋一个值,求一个点集,点集内的所有点都必须包含它的所有后继,使这个点集的和最大。

如以下图:

图中的编号代表点权。

可以知道的是,能选择的点集有:\(\left\{-3\right\},\left\{-3,4\right\},\left\{-3,5\right\},\left\{-3,4,5\right\},\left\{-3,4,5,-1\right\},\left\{-3,4,5,-1,2\right\},\emptyset\)。

最大的明显为 \(\left\{-3,4,5,-1,2\right\}\),为 \(-3+4+5-1+2=7\)。

这种类型的题我们如何解决呢,首先对于点权为负的,我们对其于汇点建边,而为正的点则用源点与其建边,其他边不变,原来图上的边设为正无穷,而新建的边为点权的绝对值。

没有设边权的代表边权无限大。

那么我们在这上面跑一次最小割,然后用所有从源点出发的边权和减去最小割即可,如图中,边权和为 \(11\),最小割为 \(4\),那么答案即为 \(7\)。

考虑证明:

首先割掉一条源点上的边代表你不会选择这个点,而割掉连向汇点上的一条边代表你会选择这个点。

很好说明,因为在割去汇点上的边时,代表你要选这个点,且正点也用流量与负点进行了抵消。

如果图不联通,那么选的点集一定合法,因为如果联通,说明你选择了正点,而正点有负点后继没有进行选择。

所以综上所言,我们就可以用最小割求出这个问题了。

那么如何证明这是最优解呢。

我们知道:最小割为:\(\text{不选的正权}+\text{要选的负权的绝对值}\)。

而答案为:\(\text{正权和}-\text{不选的正权和}+\text{选择的负权和}\),后面那个即是最小割,由于最小割保证了最小,所以答案也就最大。


那么这个最大权闭合子图有什么用呢,那就先看到我们这道题:CF311E Biologist

这题一下貌似没什么思路,先进行分析。

首先我们假设所有点初始为 \(1\),那么所有的 \(1\) 的询问我们都是满足的。

现在我们想满足一些 \(0\) 的询问,那么对于本来是 \(0\) 的点,等同于我们需要把点权加回来,而本来是 \(1\) 的点,则要扣去其费用。

对于本来是 \(0\) 的询问,满足以后要加回本应有的钱,如果要倒扣也要加回来。

而对于本来是 \(1\) 的询问,由于点变为 \(0\) 导致无法满足,所以将其全部扣除应得的钱。

那么可以把加操作看成点权为正,源点与其相连,而减操作看成点权为负,与汇点相连。

那么这次的最后答案是什么呢。

首先最小割为:\(\text{没加上的点}+\text{减去的点的绝对值}\)。

而由于我们假使所有数为 \(1\),所以答案为 \(1\) 询问减去修改 \(0->1\) 再减去 \(0\) 的倒扣钱。而这次与源点连接的所有边权和为 \(0\) 询问加上修改 \(0->1\) 加上 \(0\) 的倒扣钱。

两者加起来即为所有询问的价值,即 \(\sum W_i\)。

最终答案即为 \(\sum W_i-\text{最小割}\)。

总结一下总过程:

1、对原来为 \(0\) 的点,用源点与其建边,边权为其改变状态的价值。

2、对原来为 \(1\) 的边,对汇点建边,边权为其改变状态的价值。

3、对于每一个 \(0\) 询问,单独重新创一个点,用源点与其相连,边权为其钱数,如果要倒扣,边权加上倒扣的钱即可,然后用这个新点对询问的点建边,边权为无限(即如果选这个询问它的后继都要选择改变状态)。

4、对于每一个 \(1\) 询问,单独创一个点,对汇点建边,边权为其钱数,如果要倒扣,边权加上倒扣的钱即可,然后用询问的点对这个新点建边,边权为无限(即如果要选择一个连向新点的点,那就要扣除这个点)。

建了边后跑最小割即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e6+5;
const int M=2e6+5;
int n,m,g,h[M],p[N],s,t,st[N],cnt,vis[N],dep[N];
struct node
{
	int to,data,next;
}a[M];
inline void addedge(int x,int y,int k)
{
	a[cnt]={y,k,h[x]};
	h[x]=cnt++;
	a[cnt]={x,0,h[y]};
	h[y]=cnt++;
}
queue<int>q;
bool bfs()
{
	//cout<<'b';
	for(int i=1;i<=n+m+2;i++)dep[i]=0;
	q.push(s);
	dep[s]=1;
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=h[x];i!=-1;i=a[i].next)
		{
			if(a[i].data==0)continue;
			if(!dep[a[i].to])
			{
				dep[a[i].to]=dep[x]+1;
				q.push(a[i].to);
			}
		}
	}
	return dep[t];
}
int dfs(int x=s,int num=1e9)
{
	if(x==t)return num;
	if(vis[x])return 0;
	for(int i=h[x];i!=-1;i=a[i].next)
	{
		if(a[i].data==0)continue;
		if(dep[a[i].to]==dep[x]+1)
		{
			vis[x]=1;
			int sum=dfs(a[i].to,min(num,a[i].data));
			if(sum)
			{
				a[i].data-=sum;
				a[i^1].data+=sum;
				return sum;
			}
		}
	}
	return 0;
}
int main()
{
	memset(h,-1,sizeof(h));
	scanf("%d%d%d",&n,&m,&g);
	s=n+m+1;
	t=n+m+2;
	for(int i=1;i<=n;i++)scanf("%d",&p[i]);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		if(p[i]==0)addedge(s,i,x);
		else addedge(i,t,x);
	}
	int ans=0;
	for(int i=1;i<=m;i++)
	{
		int opt,w,k,ss;
		scanf("%d%d%d",&opt,&w,&k);
		ans+=w;
		for(int j=1;j<=k;j++)scanf("%d",&st[j]);
		scanf("%d",&ss);
		for(int j=1;j<=k;j++)
		{
			if(opt==0)addedge(i+n,st[j],w+ss*g);
			else addedge(st[j],i+n,w+ss*g);
		}
		if(opt==0)addedge(s,i+n,w+ss*g);
		else addedge(i+n,t,w+ss*g);
	}
	while(bfs())
	{
		memset(vis,0,sizeof(vis));
		int num=dfs();
		while(num)
		{
			ans-=num;
			memset(vis,0,sizeof(vis));
			num=dfs();
		}
	}
	printf("%d",ans);
	return 0;
}

标签:right,最大,int,text,边权,源点,子图,闭合,建边
From: https://www.cnblogs.com/gmtfff/p/17151282.html

相关文章