CodeForces - 742D Arpa's weak amphitheater and Mehrdad's valuable Hoses(分组背包+并查集)

CodeForces - 742D  Arpa's weak amphitheater and Mehrdad's valuable Hoses


   题意:n个人通过朋友关系分为若干组,每个人都有重量和美丽值,选取人在舞台承重量为W上跳舞,在不超过W的情况下求出最大美丽值,在每一个组里,要么挑选一个人去跳舞,要么挑选所有人都去跳舞,要么都不挑选

   思路:分组背包模板题,分组背包算法如下(摘自背包九讲)
   

    有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

算法

     这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设f[k][v]表示前k组物品花费费用v能取得的最大权值,则有:

f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于第k组}

使用一维数组的伪代码如下:

for 所有的组k

    for v=V..0

        for 所有的i属于组k

            f[v]=max{f[v],f[v-c[i]]+w[i]}

注意这里的三层循环的顺序,甚至在本文的beta版中我自己都写错了。“for v=V..0”这一层循环必须在“for 所有的i属于组k”之外。这样才能保证每一组内的物品最多只有一个会被添加到背包中。

另外,显然可以对每组内的物品应用P02中“一个简单有效的优化”。

小结

分组的背包问题将彼此互斥的若干物品称为一个组,这建立了一个很好的模型。不少背包问题的变形都可以转化为分组的背包问题(例如P07),由分组的背包问题进一步可定义“泛化物品”的概念,十分有利于解题

利用并查集求出总共用多少组,再把每一组的所有人重量之和,美丽值之和当作一个' 泛化物品 '添加到这组里

#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
int f[1001],w[1001],b[1001];
int len[1001]; 
ll dp[100005];
struct hose{
	int w;
	ll b;
}h[1001][1001]; 
int find(int x)
{
	if(x==f[x]) return x;
	else{
		f[x]=find(f[x]);
		return f[x];
	}
}
void merge(int x,int y)
{
	int fx=find(x),fy=find(y);
	if(fx!=fy) f[fy]=fx;
}
int main(void)
{
	int n,m,V;
	int x,y;
	int k=0,kcnt;
	ll sumw,sumb;
	scanf("%d%d%d",&n,&m,&V);
	for(int i=1;i<=n;i++){
	    scanf("%d",&w[i]);
	    f[i]=i;
	}
	for(int i=1;i<=n;i++)
	    scanf("%d",&b[i]);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		merge(x,y);
    }
    
	for(int i=1;i<=n;i++){
		if(f[i]==i){          //找到新的一组 
			sumw=0,sumb=0;    //泛化物品,在这一组里所有人的重量之和,美丽值之和 
			k++;        //组数加一 
			kcnt=1;     //每一组有多少人 
			h[k][kcnt].w=w[i]; 
			h[k][kcnt].b=b[i];
			sumw+=w[i],sumb+=b[i];
		}
		for(int j=1;j<=n;j++) 
		{
			if(j!=i&&find(j)==i){
				kcnt++;
				h[k][kcnt].w=w[j];
				h[k][kcnt].b=b[j];
				sumw+=w[j],sumb+=b[j];
			}
		}
		//最后把泛化物品添加到这组里 
		kcnt++;
		h[k][kcnt].w=sumw;
		h[k][kcnt].b=sumb;
		len[k]=kcnt;
	}
	//分组背包模板 
	for(int i=1;i<=k;i++)
	    for(int j=V;j>=0;j--)
		    for(int p=1;p<=len[i];p++){
		    	if(j>=h[i][p].w)
		    	   dp[j]=max(dp[j],dp[j-h[i][p].w]+h[i][p].b);
		    }
	printf("%lld\n",dp[V]);
	return 0;			    
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值