HJ16 购物单

341 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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

  1. 将输入的信息记录, 总钱数 1000, 共有 5 件物品。处理过的数据如下:
{
  "1": [
    [80, 160],
    [40, 200],
    [30, 150]
  ],
  "4": [[40, 120]],
  "5": [[50, 100]]
}

  1. 将问题分解为01背包问题。各个money所对应的最大价值。

第一组得出的 money数组和v :

money : [80, 120, 110, 150]
v: [160, 360, 310, 510]

第二组

money : [40]
v: [120]

第三组

money : [50]
v: [100]
  1. 输出