描述
王强今天很开心,公司发给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的背包,价值总和最大是多少。