购物单(背包问题)

购物单

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 回复 分享
发布于 2021-08-29 22:03
好难,看完了,还是写不出来,学废了
1 回复 分享
发布于 2022-01-13 14:26
楼主这里是不是笔误?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
为什么后面用dp[i][j]而不是dp[i-1][j]了呢
2 回复 分享
发布于 2021-07-16 23:13
我对题目有点不理解呢,一个主件只能有两个附件,但附件没有说规定只能有两种吧,我看大家的答案好像都没考虑两种以上附件的情况呢,又或者说这里的两个附件实际指两种附件而不是规定附件数量的话,那就意味着一个主件可以有无限多个附件,这种情况应该是用动态规划算不出来的,麻烦大家指点下呢~~
2 回复 分享
发布于 2021-08-03 21:38
太牛了,我只能说大佬真强,我悟了
1 回复 分享
发布于 2021-08-06 18:10
j>=a+c之后的取最大值为什么不是dp[i-1][j]啊?
1 回复 分享
发布于 2021-08-21 21:30
https://blog.nowcoder.net/n/d0ec489a235948c184f43cc17a51b3f7 请问下,这个为啥只通过了7个案例?
1 回复 分享
发布于 2021-09-12 10:58
我觉得这个是我最好理解的答案了
点赞 回复 分享
发布于 2021-01-05 19:22
这个答案思路清晰
点赞 回复 分享
发布于 2021-05-07 16:33
兄弟写的真好
点赞 回复 分享
发布于 2021-05-19 14:05
最好的解释
点赞 回复 分享
发布于 2021-05-20 11:23
写的太好了,是最好理解的一个版本了
点赞 回复 分享
发布于 2021-07-26 16:58
dp[i-1][j-a] 这个数组不会越界吗
点赞 回复 分享
发布于 2021-08-09 17:51
太厉害了,华科大佬牛逼
点赞 回复 分享
发布于 2021-08-10 17:14
谢谢大佬!清晰易懂!学到了!!!按照这个写了一个python版本: # relation: dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i]] + v[i] * w[i]), the second one is only when j >= v[i] # base1: dp[..][0] = 0 # base2: dp[0][..] = 0 N, m = map(int, input().split()) # because 每件物品的价格 都是 10 元的整数倍, so compress the search space N = int(N/10) # full cases are 4: primary only, # primary + first accessary, primary + second accessary,, primary + first+ second accessary # therefore, create a space with size 3 for price and weight_value to save the info price = [[0] * 3 for _ in range(61)] weight_value = [[0] * 3 for _ in range(61)] dp = [[0] * (N+1) for _ in range(m+1)] for i in range(1, m+1): x, y, z = map(int, input().split()) x = int(x/10) if z == 0: price[i][0] = x weight_value[i][0] = x * y elif price[z][1] == 0: # because 第 j 行给出了编号为 j-1 的物品的基本数据 price[z][1] = x weight_value[z][1] = x * y else: price[z][2] = x weight_value[z][2] = x * y # print(price) # print(weight_value) for i in range(1, m+1): for j in range(1, N+1): a, b, c, d, e, f = price[i][0], price[i][1], price[i][2], weight_value[i][0], weight_value[i][1], weight_value[i][2] if j >= a: dp[i][j] = max(dp[i-1][j], dp[i-1][j-a]+d) else: dp[i][j] = dp[i-1][j] if j >= a+b: dp[i][j] = max(dp[i][j], dp[i-1][j-a-b]+d+e) else: dp[i][j] = dp[i][j] if j >= a+b+c: dp[i][j] = max(dp[i][j], dp[i-1][j-a-b-c]+d+e+f) else: dp[i][j] = dp[i][j] print(dp[m][N] * 10)
点赞 回复 分享
发布于 2021-08-17 23:26
dp[i-1][k] -->可否理解为,选择不包含当前物品的weight*price?
点赞 回复 分享
发布于 2021-08-20 21:32
从22行开始是什么意思啊 附件
点赞 回复 分享
发布于 2021-09-10 19:22
这个题里的m不是总的件数吗,楼主的dp[m][N]的m不是说的m是主件个数吗,状态转移也是i - 1
点赞 回复 分享
发布于 2021-10-03 08:40

相关推荐

accaacc:2到4k,不是2k到4k,所以年薪是30块
点赞 评论 收藏
分享
10-09 09:39
门头沟学院 C++
HHHHaos:这也太虚了,工资就一半是真的
点赞 评论 收藏
分享
324 88 评论
分享
牛客网
牛客企业服务