购物单(背包问题)

购物单

http://www.nowcoder.com/questionTerminal/f9c6f980eeec43ef85be20755ddbeaf4

出题者觉得0/1背包太套路了,因此给我们使了点小绊子,但是问题不大。

设主件个数为n,奖金数量为M,每个主件对应的价格为v,每个主件对应的重要程度为w。d[i][j]表示从前i个主件中选取,奖金数量为j的情况下,所获得的最大价格*重要程度累加和。另外注意到一个小细节:每个主件只能有0~2个附件,最多才4种搭配方式(00,01,10,11),得到如下状态公式:

  1. 如果j>=v[i-1],那么dp[i][j] = max(dp[i][j-v[i-1]]+v[i-1]*w[i-1], dp[i-1][j], ➜➜➜➜)➜➜➜➜表示有附件的情况,为了简化问题,我们把它放到下面讲)
  2. 如果j<v[i-1],那么dp[i][j] = dp[i-1][j]
  3. 基准1: dp[0][..]=0
  4. 基准2: dp[..][0]=0

对于状态公式的解释:

  1. 如果总奖金数能涵盖当前物品,那么选取包含当前物品和不包含当前物品两种情况下最大的累加和
  2. 如果总奖金数不能涵盖当前物品,那么直接取前i-1个物品的最大累加和
  3. 基准1: 商品个数为0,再怎么加也是0
  4. 基准2: 总奖金数为0,同样再怎么加也是0

其实上面的这些,除了➜➜➜➜之外的其他部分,和0/1背包问题完全一致,接下来我们分析➜➜➜➜:

  1. 如果没有附件,则跳过
  2. 如果附件数为1,且总奖金容得下附件,那么取最大值
  3. 如果附件数为2...

通过合理构建数组,可以大大简化代码,并且提高运行效率。如下面的代码中,我们预先为prices和priceMultiplyPriority两个矩阵开辟了空间。

代码如下:

//
// Created by jt on 2020/9/5.
// 题目来源:华为机试——牛客HJ16——购物单
#include <iostream>
#include <vector>
using namespace std;

int main() {
    int N, m;
    cin >> N >> m;
    // 由于价格是10的整数倍,处理一下以降低空间/时间复杂度
    N /= 10;
    vector<vector<int> > prices(61, vector<int>(3, 0));        // 价格
    vector<vector<int> > priceMultiplyPriority(61, vector<int>(3, 0));      // 重要程度
    for (int i = 1; i <= m; ++i) {
        int a, b, c;
        cin >> a >> b >> c;
        a /= 10; b *= a;
        if (c == 0) {
            prices[i][0] = a; priceMultiplyPriority[i][0] = b;
        } else {
            if (prices[c][1] == 0) {
                prices[c][1] = a; priceMultiplyPriority[c][1] = b;
            } else {
                prices[c][2] = a; priceMultiplyPriority[c][2] = b;
            }
        }
    }
    // 使用分组背包
    vector<vector<int> > dp(m+1, vector<int>(N+1, 0));
    for (int i = 1; i <= m; ++i) {
        for (int j = 1; j <= N; ++j) {
            int a = prices[i][0], b = priceMultiplyPriority[i][0];
            int c = prices[i][1], d = priceMultiplyPriority[i][1];
            int e = prices[i][2], f = priceMultiplyPriority[i][2];
            dp[i][j] = j >= a ? max(dp[i-1][j-a] + b, dp[i-1][j]) : dp[i-1][j];
            dp[i][j] = j >= (a+c) ? max(dp[i-1][j-a-c] + b + d, dp[i][j]) : dp[i][j];
            dp[i][j] = j >= (a+e) ? max(dp[i-1][j-a-e] + b + f, dp[i][j]) : dp[i][j];
            dp[i][j] = j >= (a+c+e) ? max(dp[i-1][j-a-c-e] + b + d + f, dp[i][j]) : dp[i][j];
        }
    }
    cout << dp[m][N] * 10 << endl;
}
刷遍天下无敌手 文章被收录于专栏

秋招刷题历程

全部评论
请问第22行price[c][1]是什么意思呀?
2 回复 分享
发布于 2020-11-05 19:28
好难,看完了,还是写不出来,学废了
1 回复 分享
发布于 2022-01-13 14:26
没有附件的为什么要跳过呢?没有附加的可能是附件,附件怎么能单独购买呢?
1 回复 分享
发布于 2021-08-29 22:03
楼主这里是不是笔误?dp[i][j] = max(dp[i][j-v[i-1]]+v[i-1]*w[i-1], dp[i-1][j], ➜➜➜➜)(➜➜➜➜表示有附件的情况,为了简化问题,我们把它放到下面讲)这一句中的dp[i][j-v[i-1]]+v[i-1]*w[i-1]按照楼主的意思应该是如果总奖金数能涵盖当前物品,那么选取包含当前物品?但是顺着这个思路向下看的时候v[i-1]*w[i-1]不是应该表示的是第i件物品的价值*重要程度吗?这里为什么要用i-1呢?第i件物品不是应该是v[i]*w[i]吗?如果按i-1算,那前面的dp好像也不对啊?
5 回复 分享
发布于 2021-08-14 16:49
我对题目有点不理解呢,一个主件只能有两个附件,但附件没有说规定只能有两种吧,我看大家的答案好像都没考虑两种以上附件的情况呢,又或者说这里的两个附件实际指两种附件而不是规定附件数量的话,那就意味着一个主件可以有无限多个附件,这种情况应该是用动态规划算不出来的,麻烦大家指点下呢~~
2 回复 分享
发布于 2021-08-03 21:38
为什么后面用dp[i][j]而不是dp[i-1][j]了呢
2 回复 分享
发布于 2021-07-16 23:13
https://blog.nowcoder.net/n/d0ec489a235948c184f43cc17a51b3f7 请问下,这个为啥只通过了7个案例?
1 回复 分享
发布于 2021-09-12 10:58
j>=a+c之后的取最大值为什么不是dp[i-1][j]啊?
1 回复 分享
发布于 2021-08-21 21:30
太牛了,我只能说大佬真强,我悟了
1 回复 分享
发布于 2021-08-06 18:10
感谢dalao,十分清晰了
点赞 回复 分享
发布于 2023-03-25 15:34 陕西
代码优秀的地方是书写很规范,思路很清晰,感谢作者。
点赞 回复 分享
发布于 2023-03-23 11:01 湖南
new bee!
点赞 回复 分享
发布于 2023-03-13 14:36 新疆
能说下dp[i][j]这种写法原理吗?
点赞 回复 分享
发布于 2022-11-07 15:15 陕西
公式错漏百出……
点赞 回复 分享
发布于 2022-10-02 12:20 广东
dp[i][j]不应该取四个状态中的最大那个吗?
点赞 回复 分享
发布于 2022-07-24 15:24
dp[i][j] = j >= a ? max(dp[i-1][j-a] + a*b, dp[i-1][j]) : dp[i-1][j]; 代码中应该要加上a*吧
点赞 回复 分享
发布于 2022-06-20 14:46
好吧!!!
点赞 回复 分享
发布于 2022-06-11 01:51
这个好像没有检测商品重复购买的问题吧?1000块,第4样400块权重3,在检测金钱400和800时分别买了第四样,总满意度为2400,大于2200
点赞 回复 分享
发布于 2022-05-29 11:07
32和33之间插入这段代码,或许会快一些 if (prices[i][0] == 0) { // 没有这个序号的商品,没有买与不买之分 dp[i][j] = dp[i - 1][j]; continue; // 快速跳到下个循环 }
点赞 回复 分享
发布于 2022-04-21 22:05
其实我觉得这道题背包问题倒是其次,如何分组才是最难的地方,写分组部分的代码花费的时间比dp部分的代码多得多
点赞 回复 分享
发布于 2022-04-19 14:11

相关推荐

Yki_:你要算时间成本呀,研究生两三年,博士三四年,加起来就五六年了,如果你本科去腾讯干五年,多领五年的年薪,加上公司内涨薪,可能到时候十五年总薪资也跟博士差不多
点赞 评论 收藏
分享
06-19 19:06
门头沟学院 Java
点赞 评论 收藏
分享
评论
338
88
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务