搭配购买
[NOIP2005 普及组] 采药
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入格式
第一行有
接下来的
输出格式
输出在规定的时间内可以采到的草药的最大总价值。
样例 #1
样例输入 #1
70 3
71 100
69 1
1 2
样例输出 #1
3
提示
【数据范围】
- 对于
的数据, ; - 对于全部的数据,
。
【题目来源】
NOIP 2005 普及组第三题
题解
太弱了,先写一个基本的01背包练手
二维dp写法,在下文嵌套循环中,第一层处
需时间),
#include <iostream>
using namespace std;
int T, M; // 总共可用时间 T 和草药数量 M
int dp[1001][1001]; // dp[i][j]前 i 种草药在时间 j 内的最大价值
int t[1001], m[1001]; // 草药时间,价值
int main()
{
//可用时间,草药数量
cin >> T >> M;
for (int i = 1; i <= M; i++) {
//第i株草药的时间和价值
cin >> t[i] >> m[i];
for (int j = 0; j <= T; j++) {
// 如果当前时间 j 小于所需时间 t[i],不能采摘该草药
if (j < t[i]) {
dp[i][j] = dp[i - 1][j]; // 价值与不采摘该草药时相同
}
//反之,可以采摘
else {
// 选择采摘该草药与不采摘该草药的最大价值
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - t[i]] + m[i]);
}
}
}
// 输出在时间 T 内可以获得的最大总价值
cout << dp[M][T] << endl; // 取最后一行最后一列的值
return 0;
}
然后是一维dp写法,
#include <iostream>
using namespace std;
int T, M; // 总共可用时间 T 和草药数量 M
int dp[1001]; // dp[j]在时间 j 内的最大价值
int t[1001], m[1001]; // 草药时间,价值
int main()
{
//可用时间,草药数量
cin >> T >> M;
for (int i = 1; i <= M; i++) {
//第i种草药的时间和价值
cin >> t[i] >> m[i];
for (int j = T; j >= t[i]; j--) {
// 选择采摘该草药与不采摘该草药的最大价值
dp[j] = max(dp[j], dp[j - t[i]] + m[i]);
}
}
// 输出在时间 T 内可以获得的最大总价值
cout << dp[T] << endl; // 取最后一项的值
return 0;
}
搭配购买
题目描述
明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有
输入格式
第一行输入三个整数,
第二行至
第
输出格式
一行,表示可以获得的最大价值。
样例 #1
样例输入 #1
5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2
样例输出 #1
1
提示
- 对于
的数据,满足 ; - 对于
的数据,满足 , ; - 对于
的数据,满足 , 。
题解
题目中“买第
对此,可以用并查集将每一组搭配的云朵合并,再将价格和价值加到根节点的云朵上,并清零被合并云朵的价格价值。
由于内存限制,只能用一维dp数组
代码
#include<iostream>
using namespace std;
const int M = 1e4 + 5;
// n云朵的数量,m搭配数量,w总预算
int n, m, w;
// c[i]:第i朵价格,d[i]:第i朵价值
int c[M], d[M], dp[M]; // dp数组用于动态规划存储每个预算下的最大价值
int pre[M]; // 父节点
int root(int x);
int main()
{
//n数量,m搭配数量,w总预算
cin >> n >> m >> w;
// 读取每朵价格,价值
for (int i = 1; i <= n; i++) {
cin >> c[i] >> d[i]; // c[i]为价格,d[i]为价值
pre[i] = i; // 初始化,设每个云的父节点为自己
}
for (int k = 1; k <= m; k++) {
int a, b; // 读取云朵
cin >> a >> b;
int x = root(a), y = root(b); // 找根节点
// 如果不是同一集合
if (x ^ y) {
// 合并这两个云的价格和价值
c[y] += c[x]; d[y] += d[x];
c[x] = d[x] = 0; // 清空被合并的云朵的价格和价值
pre[x] = y; // 令x的父节点指向y
}
}
// 动态规划:计算在预算w的条件下,能获得的最大价值
for (int i = 1; i <= n; i++) {
if (d[i]) // 如果第i朵云朵的价值不为0
// 遍历预算,当买得起时
for (int j = w; j >= c[i]; j--) {
// 更新dp数组,选择购买或不购买当前云的情况
dp[j] = max(dp[j], dp[j - c[i]] + d[i]);
}
}
// 查找在所有预算中的最大价值
int ans = 0;
for (int i = 1; i <= w; i++) {
ans = max(ans, dp[i]); // 更新答案为最大价值
}
// 输出最大价值
cout << ans;
return 0;
}
// 查找并查集根节点的函数,使用路径压缩优化
int root(int x)
{
if (pre[x] == x) return x; // 如果x是根节点,直接返回
return pre[x] = root(pre[x]); // 递归查找,并路径压缩
}