搭配购买

[NOIP2005 普及组] 采药

题目描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

输入格式

第一行有 2 个整数 T1T1000)和 M1M100),用一个空格隔开,T 代表总共能够用来采药的时间,M 代表山洞里的草药的数目。

接下来的 M 行每行包括两个在 1100 之间(包括 1100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出在规定的时间内可以采到的草药的最大总价值。

样例 #1

样例输入 #1

70 3
71 100
69 1
1 2

样例输出 #1

3

提示

【数据范围】

  • 对于 30% 的数据,M10
  • 对于全部的数据,M100

【题目来源】

NOIP 2005 普及组第三题




题解

太弱了,先写一个基本的01背包练手

二维dp写法,在下文嵌套循环中,第一层处 i 用来遍历每株草药,第二层用 j 遍历每时长,

dp[i][j] 在每次循环中,记录尝试并入每一株草药后的信息:i 代表尝试并入的草药株数,j 代表所用时间(可能大于所有已经并入的草药

需时间),dp[i][j]的值 代表此时总的价值

#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;  
}



搭配购买

题目描述

明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有 n 朵云,云朵已经被老板编号为 1,2,3,...,n,并且每朵云都有一个价值,但是商店的老板是个很奇怪的人,他会告诉你一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买,电脑组的你觉得这礼物实在是太新奇了,但是你的钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云。

输入格式

第一行输入三个整数,n,m,w,表示有 n 朵云,m 个搭配和你现有的钱的数目。

第二行至 n+1 行,每行有两个整数, ci,di,表示第 i 朵云的价钱和价值。

n+2n+1+m 行 ,每行有两个整数 ui,vi。表示买第 ui 朵云就必须买第 vi 朵云,同理,如果买第 vi 朵就必须买第 ui 朵。

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

1

提示

  • 对于 30% 的数据,满足 1n100
  • 对于 50% 的数据,满足 1n,w1031m100
  • 对于 100% 的数据,满足 1n,w1040m5×103



题解

题目中“买第 ui 朵云就必须买第 vi 朵云,同理,如果买第 vi 朵就必须买第 ui 朵”将搭配的云朵捆绑,可以看作是同一个云朵

对此,可以用并查集将每一组搭配的云朵合并,再将价格和价值加到根节点的云朵上,并清零被合并云朵的价格价值。

由于内存限制,只能用一维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]); // 递归查找,并路径压缩
}
posted @   风掣凧浮  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 为DeepSeek添加本地知识库
· 精选4款基于.NET开源、功能强大的通讯调试工具
· DeepSeek智能编程
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
点击右上角即可分享
微信分享提示