[背包dp] 搭配购买

题目描述

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

输入格式

1 1 1 n , m , w n,m,w n,m,w ,表示 n n n 朵云, m m m 个搭配, J o e Joe Joe w w w 的钱。
2 ∼ n + 1 2 \sim n + 1 2n+1 行,每行 c i , d i c_i,d_i ci,di 表示 i i i 朵云的价钱和价值。
n + 2 ∼ n + m + 1 n + 2 \sim n + m + 1 n+2n+m+1 行,每行 u i , v i u_i,v_i ui,vi ,表示买 u i u_i ui 就必须买 v i v_i vi,同理,如果买 v i v_i vi 就必须买 u i u_i ui

输出格式

一行,表示可以获得的最大价值。

样例

样例输入1:

5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2

样例输出1:

1

数据范围

30 % 30\% 30% 的数据保证: n ≤ 100 n \le 100 n100
50 % 50\% 50% 的数据保证: n ≤ 1000 , m ≤ 100 , w ≤ 1000 n \le 1000,m \le 100,w \le 1000 n1000,m100,w1000
100 % 100\% 100% 的数据保证: n ≤ 10 , 000 , 0 ≤ m ≤ 5000 , w ≤ 10 , 000 n \le 10,000,0 \le m \le 5000,w \le 10,000 n10,000,0m5000,w10,000

题解

由于买了 c i c_i ci 就必须买 d i d_i di,所以可以把它们打包在一起,再按正常的 背包dp 做就行了。

维护可以使用并查集,如果不知道可以去搜索。

#include<bits/stdc++.h>
using namespace std;
int n, m, w;
int a[10010], b[10010];
int f[10010];
int dp[10010];
//并查集查找
int find(int x){
	if(x == f[x]){
		return x;
	}
	return f[x] = find(f[x]);
}
int main(){
	输入 n, m, w, ai, bi
	//初始并查集
	for(int i = 1; i <= n; ++ i){
		f[i] = i;
	}
	for(int i = 1; i <= m; ++ i){
		int x, y;
		scanf("%d %d", &x, &y);
		int t1 = find(x), t2 = find(y);
		f[t1] = t2;
	}
	//打包
	for(int i = 1; i <= n; ++ i){
		int t = find(i);
		if(i != t){
			a[t] += a[i];
			a[i] = 0;
			b[t] += b[i];
			b[i] = 0;
		}
	}
	//正常背包 dp
	for(int i = 1; i <= n; ++ i){
		for(int j = w; j >= a[i]; -- j){
			dp[j] = max(dp[j], dp[j - a[i]] + b[i]);
		}
	}
	printf("%d", dp[w]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值