一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情 。
题目
王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件。 如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。 每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。 王强查到了每件物品的价格(都是 10 元的整数倍),而他只有 N 元的预算。除此之外,他给每件物品规定了一个重要度,用整数 1 ~ 5 表示。他希望在花费不超过 N 元的前提下,使自己的满意度达到最大。 满意度是指所购买的每件物品的价格与重要度的乘积的总和,假设设第ii件物品的价格为v[i]v[i],重要度为w[i]w[i],共选中了kk件物品,编号依次为j_1,j_2,...,j_kj1,j2,...,j****k,则满意度为:v[j_1]*w[j_1]+v[j_2]*w[j_2]+ … +v[j_k]*w[j_k]v[j1]∗w[j1]+v[j2]∗w[j2]+…+v[j****k]∗w[j****k]。(其中 * 为乘号) 请你帮助王强计算可获得的最大的满意度。
输入描述(该题为ACM模式)
输入的第 1 行,为两个正整数N,m,用一个空格隔开: (其中 N ( N<32000 )表示总钱数, m (m <60 )为可购买的物品的个数。) 从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q (其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号
输出描述
输出一个正整数,为张强可以获得的最大的满意度。
解题
let base = 10 //题目中最小间隔10 因为每件价格都是10元的整数倍
let [sum, num] = readline().split(' ')
sum = sum / base
let list = {}
// 将获取的部件数据主键和附件分组。
for (let i = 1; i <= num; i++) {
let [a, b, c] = readline().split(' ').map(Number);
if (c) {
list[c] = list[c] || []
list[c].push([a / base, a / base * b])
} else {
list[i] = list[i] || []
list[i].unshift([a / base, a / base * b])
}
}
let dp = new Array(sum + 1).fill(0)
Object.values(list).forEach(e => {
let money = [],
v = []
const [m, ...s] = e
money.push(m[0])
money.push(m[1])
if (s[0]) {
const [s1, s2] = s
money.push(s1[0] + m[0])
v.push(s1[1] + m[1])
if (s2) {
money.push(s2[0] + m[0])
v.push(s2[1] + m[1])
money.push(s1[0] + s2[0] + m[0])
v.push(s1[1] + s2[1] + m[1])
}
}
for (let j = sum; j; j--) { //逆序迭代
for (let s = 0; s < money.length; s++) {
if (j - money[s] >= 0) {
dp[j] = Math.max(dp[j], dp[j - money[s]] + v[s]);
}
}
}
})
console.log(dp[sum] * base)
思路
示例:
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
- 将输入的信息记录, 总钱数 1000, 共有 5 件物品。处理过的数据如下:
{
"1": [
[80, 160],
[40, 200],
[30, 150]
],
"4": [[40, 120]],
"5": [[50, 100]]
}
- 将问题分解为01背包问题。各个money所对应的最大价值。
第一组得出的 money数组和v :
money : [80, 120, 110, 150]
v: [160, 360, 310, 510]
第二组
money : [40]
v: [120]
第三组
money : [50]
v: [100]
- 输出