华为机试-HJ16 购物单

该博客讨论了一种动态规划方法,用于解决在有限预算内最大化物品价值的问题。具体情境是王强根据物品价格、重要度和附件关系制定购物单,通过动态规划算法找到最优组合,使得总价值达到最大。示例展示了如何应用动态规划实现这一目标,并给出了一个背包问题的实例作为对比,解释了动态规划在解决这类问题上的应用。

描述
王强今天很开心,公司发给N元的年终奖。王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
在这里插入图片描述
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。王强想买的东西很多,为了不超出预算,他把每件物品规定了一个重要度,分为 5 等:用整数 1 ~ 5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第 j 件物品的价格为v[j] ,重要度为 w[j] ,共选中了k件物品,编号依次为j1 , j 2 ,……, j k,则所求的总和为:
v[j 1 ]*w[j 1 ]+v[j 2 ]*w[j 2 ]+ … +v[j k ]*w[j k ]。(其中 * 为乘号)
请你帮助王强设计一个满足要求的购物单。

输入描述:

输入的第 1 行,为两个正整数,用一个空格隔开:N m
(其中 `N ( <32000 )表示总钱数, m ( <60 )为希望购买物品的个数。)
从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q
(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)

输出描述:

输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值( <200000 )。

示例1

输入:
	1000 5
	800 2 0
	400 5 1
	300 5 1
	400 3 0
	500 2 0
输出:
	2200
#include <iostream>
#include <vector>

using namespace std;

//总钱数<32000
int dp[32000];

int main() {
    int N, m;
    cin >> N >> m;
    
    //定义数组
    vector<int> zj(m + 1), zjvw(m + 1), fj1(m + 1), fj2(m + 1), fj1vw(m + 1), fj2vw(m + 1);
    //m:输入的物品数量
    for(int i = 1; i <= m; i++)
    {
        int v, p, q;
        cin >> v >> p >> q;//cin遇到空格就停止,因此可以连读输入
        //主件
        if(q == 0)
        {
            zj[i] = v;
            zjvw[i] = v * p;
        }
        //附件1
        //不是用i来定义的
        //q值对应的是从属的主件
        //fj1[q] 对应的主件i
        //else if(fj1[q] == 1 || fj1[q] == 2)
        else if(fj1[q] == 0)
        {
            fj1[q] = v;
            fj1vw[q] = v * p;
        }
        //附件2
        //else if(fj2[q] == 1 ||fj2[q] == 2)
        else if(fj2[q] == 0)
        {
            fj2[q] = v;
            fj2vw[q] = v * p;
        }
    }
    //动态规划过程
    for(int i = 1; i <= m; i++)
    {
        for(int j = N; j >=1; j--)//从高位到低位,钱越多买的越多,所以钱最多是N
        {
            //只要主件
            if(j >= zj[i])
                dp[j] = max(dp[j], dp[j - zj[i] ] + zjvw[i]);
            //买了主件+附件1
             if(j >= zj[i] + fj1[i])
                dp[j] = max(dp[j], dp[j - zj[i] - fj1[i]]  + zjvw[i] + fj1vw[i]); 
            //买了主件+附件2
             if(j >= zj[i] + fj2[i])
                dp[j] = max(dp[j], dp[j - zj[i] - fj2[i]]  + zjvw[i] + fj2vw[i]); 
            ////买了主件+附件1+附件2
             if(j >= zj[i] + fj1[i] + fj2[i])
                dp[j] = max(dp[j], dp[j - zj[i] - fj1[i] - fj2[i] ] + zjvw[i] + fj1vw[i] + fj2vw[i]); 
        }
    }
    cout <<dp[N];
    return 0;
}

01背包,动态规划
给你一个可装载容量为w的背包和N个物品,每个物品有重量和价值两个属性。其中第i个物品的重量为wt[i],价值为val[i],现在让你用这个背包装物品,最多能装的价值是多少?

N=3, W=4 
wt = [2, 1, 3],
val = [4, 2, 3]
算法返回 6

第一步、明确「状态」和「选择」

状态 :背包的空余容量剩多少 ,可选择的物品还有哪些
选择 :把这个物品装进背包 不把这个物品装进背包

第二步、明确dp数组的定义

dpl[i][w]的含义为:对于前i个物品,当背包的容量为w时,可以装的最大价值是dpl[i][w]
比如说, dp[3][5]=6的含义为:
对于给定的一系列物品中,若只对前3个物品进行选择,当背包容量为5时,最多可以装下的价值为6
根据此定义,还可得出:
base case为dp[0][..] = dp[..][0]=0,我们想计算的结果是dp[N][W]

第三步,根据「选择」写出状态转移逻辑
在这里插入图片描述

1、在w的约束下,把物品i装进背包,最大价值是多少?
i个物品的价值val[i],把第i个物品放进背包后,容量还剩w-wt[i],那么前i-1个物品的价值dp[i-1][w-wt[i]]
所以总两项相加:val[i]+dp[i-1][w-wt[i]]
2、在w的约束下,不把物品i装进背包,最大价值是多少?
dp[i-1][w]

解法代码
在这里插入图片描述

dp数组的定义

一维dp数组中:dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。
二维dp数组中:dp[i][j],dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值