CF103E Buying Sets题解

博客里阅读

最近博客里奇奇怪怪的东西越来越多了,是时候写一篇题解了

这是模拟赛的一道题,考试时只会打暴力QWQ,后来发现这道题的建图真的十分巧妙

首先,看到\(n<=300\)的数据范围,很像是网络流,我们先把子集和子集中的元素拆开来看,将子集的价值取相反数,最后输出答案时再取个相反数,于是便为选了这个子集就必须要选其中的元素,使得选出的子集价值尽量大,将子集向元素连\(inf\)边,那么这就是一个最大权闭合子图的模型,

然后难点在于如何处理使得子集并的大小等于子集个数,看到题目中给的“任意\(k\)个子集的并的大小\(k\ge k\)”,我们可以想一想如何从这个角度来建图,由源点向子集连\(lim+P_i\)的边(\(lim\)是比\(inf\)小的一个极大值,防止\(inf\)边被割掉),这条边被割意味着不选这个子集,由元素向汇点连\(lim\)边,这条边被割意味着这个元素最终会被选,这里可以感性理解一下画个图手玩一下,最终被割掉的边肯定为\(n\)条,并且任意\(k\)个子集的并的大小\(k\ge k\)

放个代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int s,t,dep[1003],n,t1,x,p,ans;
vector<int>l[1003],l1[1003],nu[1003];
queue<int>q;
void bfs()
{
	memset(dep,0,sizeof(dep));
	q.push(s),dep[s]=1;
	while(!q.empty())
	{
		p=q.front(),q.pop();
		for(int j=0;j<l[p].size();j++)
			if(l1[p][j]!=0&&dep[l[p][j]]==0)
				q.push(l[p][j]),dep[l[p][j]]=dep[p]+1;
	}
}
int dfs(int x1,int wat)
{
	if(x1==t)
		return wat;
	int sum=0,cnt;
	for(int j=0;j<l[x1].size();j++)
		if(l1[x1][j]!=0&&dep[l[x1][j]]==dep[x1]+1)
			cnt=dfs(l[x1][j],min(l1[x1][j],wat)),l1[x1][j]-=cnt,l1[l[x1][j]][nu[x1][j]]+=cnt,sum+=cnt,wat-=cnt;
	if(sum==0)
		dep[x1]=0;
	return sum;
}
void add(int x,int y,int wat)
{
	l[x].push_back(y),l[y].push_back(x),l1[x].push_back(wat),l1[y].push_back(0),nu[x].push_back(l[y].size()-1),nu[y].push_back(l[x].size()-1);
}
signed main()
{
	scanf("%lld",&n);
	s=0,t=2*n+1;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&t1);
		add(i+n,t,5e8);
		for(int j=1;j<=t1;j++)
		{
			scanf("%lld",&x);
			add(i,x+n,2e9);
		}
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&x);
		add(s,i,5e8-x),ans+=5e8-x;
	}
	while(1)
	{
		bfs();
		if(dep[t]==0)
			break;
		ans-=dfs(s,2e9);
	}
	cout<<min(0ll,-ans);
	return 0;
}

posted @ 2020-06-07 17:14  dz_ice  阅读(244)  评论(0编辑  收藏  举报