购物单 题解(HJ16) 动态规划01背包

文章描述了一种解决背包问题的策略,特别是处理主件和附件关系的情况。通过将附件合并到主件中,将问题转化为分组背包问题,然后使用动态规划求解,以找到最大价值的物品组合。代码示例展示了如何构建和更新动态规划表格以得出答案。
摘要由CSDN通过智能技术生成

牛客网链接: 题目链接

题目描述

题目描述

输入1

1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0

输出1

2200

输入2

50 5
20 3 5
20 3 5
10 3 0
10 2 0
10 1 0

输出2

130
说明:
由第1行可知总钱数N为50以及希望购买的物品个数m为5;
第2和第3行的q为5,说明它们都是编号为5的物品的附件;
第4~6行的q都为0,说明它们都是主件,它们的编号依次为3~5;
所以物品的价格与重要度乘积的总和的最大值为10*1+20*3+20*3=130  

解题思路

首先这是个背包,很容易看出来。
题眼在于对附件的处理,我一开始把附件和主件一视同仁,然后每次在选择附件时,关注主件是否被选择。
这样做的代价,就是需要开另一个数组储存每个节点选择的物品序列。
以上的方法适用于附件数目不定的条件下。(但是也挺抽象的)

本题附件最多只有2个,而且附件不能脱离主件,所以可以把附件直接加在主件的后面,变成了分组背包。
一个分组最多有四个物品,分别是:

  1. 主件
  2. 主件+附件1
  3. 主件+附件2
  4. 主键+附件1+附件2
    这样我们就可以提前计算,每种情况的花费和重要度。

题解

#include <cstdio>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
// 这个题的关键就在于对附件的处理
// 因为附件数量是有限的,所以直接把附件加在主件上
//**********附件无法单独存在,所以直接合并在主件里********

int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    n /= 10;
    //储存每种情况的花费
    vector<vector<int>> price(61, vector<int>(3, 0));
    //储存每种情况的重要度
    vector<vector<int>> value(61, vector<int>(3, 0));

    for (int i = 1; i <= m; i++)
    {
        int x, y, z;

        scanf("%d %d %d", &x, &y, &z);
        x /= 10;
        y*=x;
        // 主
        if (z == 0)
        {
            price[i][0] = x;
            value[i][0] = y;
        }
        // 从
        else
        {
            // 从1
            if (price[z][1] == 0)
            {
                price[z][1] = x;
                value[z][1] = y;
            }
            // 从2
            else
            {
                price[z][2] = x;
                value[z][2] = y;
            }
        }
    }
    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, b, c, d, e, f;
            a = price[i][0];
            b = value[i][0];
            c = price[i][1];
            d = value[i][1];
            e = price[i][2];
            f = value[i][2];
            if (j >= a)
            {
                dp[i][j] = max(dp[i - 1][j], b + dp[i - 1][j - a]);
            }
            //如果没有这个else,dp[i][j]的值会保持0,再和后面比就错了
            else
            {
                dp[i][j]=dp[i-1][j];
            }
            // 注意后面的是和dp[i][j]比较******
            if (j >= a + c)
            {
                dp[i][j] = max(dp[i][j], b + d + dp[i - 1][j - a - c]);
            }
            if (j >= a + e)
            {
                dp[i][j] = max(dp[i][j], b + f + dp[i - 1][j - a - e]);
            }
            if (j >= a + c + e)
            {
                dp[i][j] = max(dp[i][j], b + d + f + dp[i - 1][j - a - c - e]);
            }
            //实现方式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];
        }
    }
    printf("%d\n", dp[m][n] * 10);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值