51nod 1821 最优集合(贪心+并查集)

基准时间限制:1 秒 空间限制:131072 KB 分值: 40  难度:4级算法题
 收藏
 关注
一个集合S的优美值定义为:最大的x,满足对于任意i∈[1,x],都存在一个S的子集S',使得S'中元素之和为i。
给定n个集合,对于每一次询问,指定一个集合S1和一个集合S2,以及一个数k,要求选择一个S2的子集S3(|S3|<=k),使得S1∪S3的优美值最大。
(集合元素可以重复)
Input
第一行一个数n,(n<=1000)
接下来n行,每行描述一个集合:
第一个数m,表示集合大小,接下来m个数,表示集合中的元素(m<=1000,元素<=10^9)
第n+2行一个数T,表示询问次数(T<=10000)
接下来T行,每行3个数a,b,k,表示指定第a个集合为S1,第b个集合为S2,k的意义如题(a<=n,b<=n,k<=100,000)
Output
T行,每行一个数,表示对应询问所能达到的最大优美值
Input示例
2
6 1 2 3 8 15 32
6 1 1 1 1 1 1
1
1 2 3
Output示例
64
﹡    LH  (题目提供者)
Visual C++的运行时限为:1000 ms ,空间限制为:131072 KB  示例及语言说明请按这里

 允许其他 AC 的用户查看此代码,分享代码才能查看别人的代码并有机会获得勋章


对于一个集合的优美值now而言,假如存在一个集合的优美值为x并且x<=now+1的话,

呢两个集合加起来的优美值一定是now+x,因此我们在找第一个集合优美值的同时,

在第二个集合中每次贪心的找最大不超过now+1的子集合加入到集合1中,但是每次从头找回很耗时,

我们可以采取一些优化策略,比如标记这个数之前没用过的最大的数,可以用并查集搞搞。

#include<set>
#include<map>   
#include<stack>          
#include<queue>          
#include<vector>  
#include<string>
#include<math.h>          
#include<stdio.h>          
#include<iostream>          
#include<string.h>          
#include<stdlib.h>  
#include<algorithm> 
#include<functional>  
using namespace std;          
#define ll long long       
#define inf  1000000000     
#define mod 1000000007           
#define maxn  1008
#define lowbit(x) (x&-x)          
#define eps 1e-9
ll a[maxn][maxn],parent[maxn];
int read()  
{  
    char c=getchar();int x=0,f=1;  
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}  
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}  
    return x*f;  
}  
int find(int x)
{
	if(parent[x]==x)
		return x;
	return parent[x]=find(parent[x]);
}
void solve(int p,int q,int k)
{
	int i=1,j=1,x,now=0;
	while(1)
	{
		if(i<=a[p][0] && a[p][i]<=now+1)
			now+=a[p][i],i++;
		else
		{
			if(k<=0)
				break;
			k--;
			while(j<=a[q][0] && a[q][j]<=now+1)
				j++;
			j--;x=find(j);
			if(x==0)
				break;
			now+=a[q][x];
			parent[x]=find(x-1);
			if(x==j)
				j++;
		}
	}
	printf("%d\n",now);
}
int main(void)
{
	int n,k,i,j,x,y,T;
	n=read();
	for(i=1;i<=n;i++)
	{
		a[i][0]=read();
		for(j=1;j<=a[i][0];j++)
			a[i][j]=read();
		sort(a[i]+1,a[i]+a[i][0]+1);
	}
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d",&x,&y,&k);
		for(i=0;i<=1002;i++)
			parent[i]=i;
		solve(x,y,k);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值