2018宁夏邀请赛 Clever King(最大点权闭合子图学习+模板)

本文详细解析了一种求解最大点权闭合子图的问题,通过构建特定的网络流图,并利用Dinic算法寻找最小割,从而计算出最大收益。文章提供了完整的代码实现,涵盖了初始化、增广路径搜索、最大流计算等关键步骤。

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

参考博客:https://www.cnblogs.com/dilthey/p/7565206.html

题意:T组样例。n种产品,m个矿场。制造每种产品都需要一些其他产品和矿石。制造一种产品会获得val的收益,开采一个矿场需要花费cost,矿场一旦开采就能获得无穷多的矿石。问净收益的最大值。

结论:

1、最大点权闭合子图的值等于正点权和-最小割。

2、最大点权闭合子图中的节点的后继节点都在该子图中。

 思路:正权点和源点连边容量为权值,负权点和汇点连边容量也为权值,为相反数也就是正的,其余边容量都为inf。跑一遍最大流后,用正权和减去最大流即可。

#include <bits/stdc++.h>
#define  ll long long
#define int long long
using namespace std;
const int N = 1e3+10;
const int M = 1e6+10;
const int inf = 0x3f3f3f3f3f3f3f3fLL;
struct node
{
	int  to,ca,next;
}g[M];
int head[N],deep[N],cur[N],q[N],he,ta,cnt,a[210],b[210];
int n,m,s,t;
void Init()
{
	cnt=0;
	memset(head,-1,sizeof(head));
	//for(int i=1;i<=n;i++)
		//head[i]=-1;
	return ;
}
void add(int u,int v,int w)
{
	g[cnt].to=v;
	g[cnt].ca=w;
	g[cnt].next=head[u];
	head[u]=cnt++;
	return ;
}
bool bfs()
{
	memset(deep,0,sizeof(deep));
	//for(int i=1;i<=n;i++)
		//deep[i]=0;
	he=ta=0;
	int u,v;
	q[ta++]=s;
	deep[s]=1;//别漏了!!!!!!!!!!!!!!!! 
	while(he!=ta)
	{
		u=q[he++];
		//cout<<u<<endl;
		if(u==t) return 1;
		for(int i=head[u];i!=-1;i=g[i].next)
		{
			v=g[i].to;
			if(g[i].ca>0&&!deep[v])
			{
				deep[v]=deep[u]+1;
				q[ta++]=v;
			}
		}
	}
	return deep[t]!=0;
}
int dfs(int u,int flow)
{
	//cout<<u<<endl;
	if(u==t||!flow) return flow;
	int ans=0,nowflow,v;
	for(int& i=cur[u];i!=-1;i=g[i].next)
	{
		v=g[i].to;
		if(g[i].ca>0&&deep[v]==deep[u]+1)
		{
			nowflow=dfs(v,min(flow,g[i].ca));
			if(nowflow)
			{
				ans+=nowflow;
				flow-=nowflow;
				g[i].ca-=nowflow;
				g[i^1].ca+=nowflow;
				if(!flow) break;
			}	
		}
	}
	if(!ans) deep[u]=0;
	return ans;
}
int Dinic()
{
	int maxflow=0,flow;
	while(bfs())
	{
		memcpy(cur,head,sizeof(head));
		//for(int i=1;i<=n;i++)
			//cur[i]=head[i];
		while(flow=dfs(s,inf))
		{
			//cout<<flow<<endl;
			maxflow+=flow;
		}
			
	}
	//cout<<maxflow<<endl;
	return maxflow;
}
signed main(void)
{
	int T;
	scanf("%lld",&T);
	while(T--)
	{
		int sum=0;
		
		scanf("%lld%lld",&n,&m);
		s=0,t=n+m+1;
		Init();
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);
			sum+=a[i];
			add(s,i,a[i]);
			add(i,s,0);
		}
		for(int i=1;i<=m;i++)
		{
			scanf("%lld",&b[i]);
			add(i+n,t,b[i]);
			add(t,i+n,0);
		}
		for(int i=1;i<=n;i++)
		{
			int k1,k2,x;
			scanf("%lld%lld",&k1,&k2);
			for(int j=1;j<=k1;j++)
			{
				scanf("%lld",&x);
				add(i,x+n,inf);
				add(x+n,i,0);
			}
			for(int j=1;j<=k2;j++)
			{
				scanf("%lld",&x);
				add(i,x,inf);
				add(x,i,0);
			}
		}
		
		printf("%lld\n",sum-Dinic());	
	}
	
	
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值