【华为机考真题】动态规划之遗迹探险家小红(含C++源码)

0. 前言

本文要介绍的一道题是动态规划的经典变种------二维费用背包问题,这是华为机考的第三题,也就是压轴题,有一定难度,下面我们进入正题。

1. 题目描述

小红在遗迹中收集宝藏。

遗迹中共有 n 件宝藏,收集第 i 件宝藏需要花费的时间为 t_i,价值为 p_i

总探险时间不能超过 T,若 t_i > 30,则视为重型宝藏,重型宝藏的总数不得超过 m 个,并且每件宝藏只能收集一次。

满足上面限制的条件下,求小红能获得的最大价值总和。

输入和输出描述如下:

输出:一个整数,表示小红能获得的最大总价值。

2. 题目分析

在上一篇的幻兽防御战中,可以靠 排序+贪心 轻松解决。但在本题中:

  • 重型宝藏虽然价值高,但是它会占用重型宝藏的名额,并且耗时往往更长。
  • 选择了一个重型宝藏,可能会导致无法选择后面更优的两个普通宝藏的组合。
  • 当存在 互斥的多种约束 时,局部最优不等于全局最优,这时我们需要 穷举所有状态并记录最优解 ,这正是 动态规划 存在的意义。

3. 动态规划思路分析

3.1 确定dp数组的含义

本题中存在两个限制,分别是总时间 T 和重型宝藏名额 m

我们定义:dp[j][k] 代表在剩余时间为 j,剩余重型名额为 k 时,能获得的最大价值。

3.2 确定状态转移方程

对于每一件宝藏,我们需要选择拿还是不拿。

  • 如果不拿,那么 dp[j][k] 保持不变。
  • 当剩余资源 jk 允许时,我们可以选择拿,拿了之后需要判断是普通宝藏还是重型宝藏:
    • 普通宝藏t_i <= 30dp[j][k] = max(dp[j][k], dp[j - t_i][k] + p_i)
    • 重型宝藏t_i > 30dp[j][k] = max(dp[j][k], dp[j - t_i][k - 1] + p_i)

3.3 其他需要注意的点

所有的 dp[j][k] 初始化为 0,代表初始状态下没有收集任何宝藏。

为了保证每件宝藏只被收集一次,内层循环(时间 j 和名额 k)必须采用 逆序 遍历。

按照这个思路,每一轮循环都会基于上一轮的结果更新当前的最高价值。

4. 代码实现

完整代码如下:

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <sstream>

using namespace std;

int main() {
    string line;
    getline(cin, line);
    stringstream ss(line);
    int n,T,m;

    ss >> n >>T >> m;

    vector<vector<int>> dp(T+1, vector<int> (m+1, 0));

    for(int i=0; i<n; i++)
    {
        int t_i,p_i;
        getline(cin, line);
        stringstream ss1(line);
        ss1 >> t_i >> p_i;//读到每件宝藏的时间和价值

        if(t_i > 30)//重型宝藏
        {
            for(int j=T; j>=t_i; j--)
            {
                for(int k=m; k>=1; k--)
                {
                    dp[j][k] = max(dp[j][k], dp[j-t_i][k-1]+p_i);
                }
            }
        }
        else 
        {
            for(int j=T; j>=t_i; j--)
            {
                for(int k=m; k>=0; k--)
                {
                    dp[j][k] = max(dp[j][k], dp[j-t_i][k]+p_i);
                }
            }
        }
    }
    cout << dp[T][m];
    return 0;
}

5. 几类动态规划问题方法梳理

我将常见的几类动态规划问题整理成下面的表格:

相关推荐
钓鱼的肝1 小时前
csp-j-s总结(2)
c++·经验分享·笔记·算法·青少年编程
重生的黑客1 小时前
Qt 常用控件精讲(1):QWidget 核心属性全解 —— 从 geometry 到 qrc 与 QSS
c++·qt·qwidget·qss·常用控件
Hespethorn2 小时前
取 `X-Forwarded-For` 首段做频控 key,等于把限流开关交给了调用方
c++
米糕闯编程2 小时前
鱼香ros2(七)功能包组织C++节点
c++·ros2·cmakelists
汉克老师2 小时前
GESP2026年9月认证C++四级( 第三部分编程题(1、新汉诺塔))精讲
c++·gesp·小学生·学c++编程
唠玖馆3 小时前
智能指针详解
c++
ShineWinsu3 小时前
对于Git:基础操作的超详细保姆级解析
linux·c++·git·面试·备份·管理·版本控制器
小小de风呀4 小时前
de风——【从零开始学C++】(二十)红黑树封装map和set
c++·学习
青梅橘子皮4 小时前
Blue---枚举
c++
马剑威(威哥爱编程)5 小时前
【共创稿事节】HarmonyOS 7 互动卡片实战:摇一摇触发静态转动态,前景元素出框
华为·harmonyos