dp+并查集

不会dp和并查集我来水个题
在这里插入图片描述
搭配购买

Description
Joe觉得云朵很美,决定去山上的商店买一些云朵。商店里有n朵云,云朵被编号为1,2,…,n,并且每朵云都有一个价值。但是商店老板跟他说,一些云朵要搭配来买才好,所以买一朵云则与这朵云有搭配的云都要买。
但是Joe的钱有限,所以他希望买的价值越多越好。

Input
第1行n,m,w,表示n朵云,m个搭配,Joe有w的钱。
第2∼n+1行,每行ci,di表示i朵云的价钱和价值。
第n+2∼n+1+m行,每行ui,vi,表示买ui就必须买vi,同理,如果买vi就必须买ui。
Output
一行,表示可以获得的最大价值。

Samples
Input
5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2
Output
1
Hint
【数据范围】
30%的数据保证:n≤100
50%的数据保证:n≤1,000;m≤100;w≤1,000
100%的数据保证:n≤10,000;0≤m≤5000;w≤10,000

思路:把所有在一组的物品打包后,看做一个物品,然后0 1背包即可。
这里打包的方法是并查集,让同一组的物品连起来后要跑一遍find函数,把父结点统一成同一个祖先结点。
Code:

#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int N = 1e4+10;
const int inf = 0x3f3f3f;
int n,m,w;
int c[N],d[N],vis[N];
int p[N];
int dp[N],c2[N],d2[N];
int find(int x) {
	if(x==p[x]) return x;
	return p[x] = find(p[x]);
}
int main() {
	cin>>n>>m>>w;
	for(int i=1; i<=n; i++) {
		scanf("%d%d",&c[i],&d[i]);
		p[i] = i;
	}
	for(int i=1; i<=m; i++) {
		int u,v;
		scanf("%d%d",&u,&v);
		p[find(u)] = find(v);
	}
	for(int i=1;i<=n;i++) find(i);
	int k=0;
	for(int i=1; i<=n; i++) {
		if(!vis[i]) {
			++k;
			//cout<<p[i]<<"*"<<endl;
			for(int j=1; j<=n; j++) {
				if(p[j]==p[i]&&!vis[j]) {
					vis[j]=1;
					c2[k]+=c[j];
					d2[k]+=d[j];
				}
			}
		}
	}
	//cout<<k<<endl;
	for(int i=1; i<=k; i++) {
		for(int j=w; j>=c2[i]; j--) {
			dp[j] = max(dp[j],dp[j-c2[i]]+d2[i]);
		}
	}
	cout<<dp[w];
	return 0;
}


明天要补英语啦,好像还得写什么有关寒假生活的的作文。
我我我…我TMD。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值