【华为机试】HJ16 购物单详解+完整源代码示例

从毕业到入职,忙于各种事情,所以博客一直也没有空更新。从入职到现在差不多整三个月了,刚刚在比亚迪这边转正。目前干的工作涉及到开发的工作不是很多,但是又怕之前的技能荒废了。所以最近有个想法,再把C++算法和数据结构的东西捡起来,练一练。
现在再做这些,反而少了一些功利心,可以单纯地去学一下算法和数据结构的东西。
先定个小目标:把华为机试这100多道算法题先干掉!然后记录一下其中中等难度及以上难度题目的解析。一方面激励自己,一方面也给自己的学习过程留个记录,后面再回顾的时候,也更方便些。

题目

【华为机试】HJ16 购物单详解+完整源代码示例_第1张图片
【华为机试】HJ16 购物单详解+完整源代码示例_第2张图片
【华为机试】HJ16 购物单详解+完整源代码示例_第3张图片

题目解析

首先这个题目比较长,比较复杂,所以理解起来也稍微有点费劲,需要仔细去读,不然很容易在理解上产生错误和偏差。

关键点

  1. 王强要购买的物品分为两类,有主件和附件两种,附件不再有自己的附件。
  2. 每个主件可以有0个,1个或者2个附件,即最多一个主件可以有两个附件。
  3. 每个物品有一个重要度,用1~5表示。
  4. 单个物品的满意度 = 价格 * 重要度
最后题目要求要使王强得到最大的满意度,并且输出是多少。
从题目来看,该题目是0-1背包问题的一个扩展。

有关0-1背包问题的动态规划,下面这篇博客写得非常详细,我这里不再赘述。
【动态规划】01背包问题(通俗易懂,超基础讲解)

这里简单说一下0-1背包里面要解决的问题以及其中定义的一些变量,方便下面来进行比较。

【华为机试】HJ16 购物单详解+完整源代码示例_第4张图片

如上图所示
  • 这里背包的容量就相当于王强所拥有的总钱数N。
  • 物体的体积或者说重量w相当于该题目中物品的价格。
  • 而价值v相当于该题目中的满意度。
另外该问题与0-1背包问题的一个区别就是:
  • 0-1背包问题对于物品要考虑的就是两种情况:
    • 不选
  • 该题目则是有五种情况:
    • 不买
    • 只买主件
    • 买主件 + 附件1
    • 买主件 + 附件2
    • 买主件 + 附件1 + 附件2
所以,解这个题目需要先重新把这个表填了。
令v[i]表示第i个物品的价格:
  • v[i][0] 表示物品是主件
  • v[i][1] 表示物品是附件1
  • v[i][2] 表示物品是附件2
令w[i]表示第i个物品的满意度:
  • w[i][0] 表示物品是主件
  • w[i][1] 表示物品是附件1
  • w[i][2] 表示物品是附件2
令dp[i][j] 表示当前钱数为j,前i个物品最佳组合对应的价值。
状态转移方程

dp[i][j] = max(

dp[i-1][j] ,(不买第i件物品)

dp[i-1][j-v[i][0]]+w[i][0] ,(买第i件物品)

dp[i-1][j-v[i][0]-v[i][1]]+w[i][0]+w[i][1], (买第i件物品与其附件1)

dp[i-1][j-v[i][0]-v[i][2]]+w[i][0]+w[i][2],(买第i件物品与其附件2)

dp[i-1][j-v[i][0]-v[i][1]-v[i][2]]+w[i][0]+w[i][1]+w[i][2]).(买第i件物品与其两件附件)

需要注意的几个点:
  • 物品编号从1开始
  • 遍历顺序为先遍历背包,再遍历物品,这是因为有主件附件等不同情况的原因。
  • 初始化的边界条件均为0,即dp[0][……]和dp[……][0]均为0
源代码
#include 
#include 
#include 

using namespace std;

int main() {
    int N, m;
    cin >> N >> m;
    vector<vector<int>> v(m + 1, vector<int>(3, 0));  // 存价格,相当于0-1背包中的W
    vector<vector<int>> w(m + 1, vector<int>(3, 0));  // 存价格*重要度,即满意度,相当于0-1背包中的V
    vector<vector<int>> dp(m + 1, vector<int>(N + 1, 0)); 
    N /= 10;
    for (int i = 1; i < m + 1; i++) { // 物品编号从1开始,q为0表示主件,为1表示第1件物品的附件
        int vv, p, q;
        cin >> vv >> p >> q;
        vv /= 10; // 简化计算,加快计算速度
        if (q) {  // 如果q不为0,则说明第i个物品是附件
            if (w[q][1]) { // 如果w[q][1]不为0,则说明不是第一个附件
                v[q][2] = vv; 
                w[q][2] = vv * p;
            } else { // 否则就是第一个附件
                v[q][1] = vv;
                w[q][1] = vv * p;
            }
        } else { // 否则,第i个物品是主件
            v[i][0] = vv;
            w[i][0] = vv * p;
        }
    }
    for (int j = 1; j < N + 1; j++) { // 遍历背包
        for (int i = 1; i < m + 1; i++) { // 遍历物品
            if (v[i][0] > j) {
                dp[i][j] = dp[i-1][j];  // 都不买
            } else {
                if (v[i][0] <= j) { // 只买主件
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i][0]]+w[i][0]);
                }
                if (v[i][0]+v[i][1] <= j) { // 买主件+附件1
                    dp[i][j] = max(dp[i][j], dp[i-1][j-v[i][0]-v[i][1]]+w[i][0]+w[i][1]);
                }
                if (v[i][0]+v[i][2] <= j) { // 买主件+附件2
                    dp[i][j] = max(dp[i][j], dp[i-1][j-v[i][0]-v[i][2]]+w[i][0]+w[i][2]);
                }
                if (v[i][0]+v[i][1]+v[i][2] <= j) { // 买主件+附件1+附件2
                    dp[i][j] = max(dp[i][j], dp[i-1][j-v[i][0]-v[i][1]-v[i][2]]+w[i][0]+w[i][1]+w[i][2]);
                }
            }    
        }
    }
    cout << dp[m][N] * 10 << endl;
    return 0;
}
参考文献

【华为机试】HJ16 购物单

你可能感兴趣的:(C/C++,华为,华为od,C++算法,华为机试,后端开发)