【华为机试刷题笔记】HJ16-购物单

【华为机试刷题笔记】HJ16-购物单_第1张图片
王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

主件 附件
电脑 打印机,扫描仪
书柜 图书
书桌 台灯,文具
工作椅

如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。
每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。
王强查到了每件物品的价格(都是 10 元的整数倍),而他只有 N 元的预算。除此之外,他给每件物品规定了一个重要度,用整数 1 ~ 5 表示。他希望在花费不超过 N 元的前提下,使自己的满意度达到最大。
满意度是指所购买的每件物品的价格与重要度的乘积的总和,假设设第 i i i件物品的价格为 v [ i ] v[i] v[i],重要度为 ω ( i ) \omega(i) ω(i),共选中了 k k k件物品,编号依次为 j 1 , j 2 , . . . , j k j_1,j_2,...,j_k j1,j2,...,jk,则满意度为: v [ j 1 ] ∗ ω [ j 1 ] + v [ j 2 ] ∗ ω [ j 2 ] + . . . + v [ j k ] ∗ ω [ j k ] v[j_1]*\omega[j_1]+v[j_2]*\omega[j_2]+...+v[j_k]*\omega[j_k] v[j1]ω[j1]+v[j2]ω[j2]+...+v[jk]ω[jk]。(其中 * 为乘号)
请你帮助王强计算可获得的最大的满意度。

输入描述:
输入的第 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 是所属主件的编号)

输出描述:
输出一个正整数,为张强可以获得的最大的满意度。

示例1

输入:1000 5
	800 2 0
	400 5 1
	300 5 1
	400 3 0
	500 2 0
输出:2200

示例2

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

题解

下面展示本人题解思路代码。

/* 
fill()方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素,不包括终止索引。
map() 方法定义在JavaScript的Array中,它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。
 */
const rl = require("readline").createInterface({ input: process.stdin })
var iter = rl[Symbol.asyncIterator]()
const readline = async () => (await iter.next()).value

void (async function () {
  //write your code here
  let line = await readline()
  //注意将接收到的数据处理为Number类型
  let [N, m] = line.split(' ').map(Number)
  //N<32000,定义总钱数所对应满意度的数组并初始化为0
  let dp = new Array(32000).fill(0)
  //主件(m<60)
  let zj = new Array(61)
  //定义二维数组并初始化
  for (let i = 1; i < m + 1; i++) {
    //每个可购买物品对应6个参数:主件价格、主件满意度、附件1价格、附件1满意度、附件2价格、附件2满意度
    zj[i] = new Array(6)
    for (let j = 0; j < 6; j++) {
      zj[i][j] = 0
    }
  }
  //接收输入(表2)
  for (let i = 1; i < m + 1; i++) {
    let line = await readline()
    //注意将接收到的数据处理为Number类型
    let [p, v, q] = line.split(' ').map(Number)
    //判断是否为主件
    if (q == 0) {
      zj[i][0] = p
      zj[i][1] = p * v
    } else {
      //附件1
      if (zj[q][2] == 0) {
        //第q件主件的附件1价格
        zj[q][2] = p
        //第q件主件的附件1满意度
        zj[q][3] = p * v
      } else {
        //附件2
        //第q件主件的附件2价格
        zj[q][4] = p
        //第q件主件的附件2满意度
        zj[q][5] = p * v
      }
    }
  }
  //动态规划(表3)
  for (let i = 1; i < m + 1; i++) {
    for (let j = N; j > 0; j -= 10) {
      //只买主件
      //判断主件价格是否大于0
      if (zj[i][0] > 0) {
        //判断j是否大于主件价格
        if (j >= zj[i][0]) {
          dp[j] = Math.max(
            dp[j],
            //第i件主件满意度+所拥有钱减去买第i件主件的价格后剩余钱的满意度
            zj[i][1] + dp[j - zj[i][0]]
          )
        }
        //主件+附件1
        if (zj[i][2] > 0 && j >= zj[i][0] + zj[i][2]) {
          dp[j] = Math.max(
            dp[j],
            zj[i][1] + zj[i][3] + dp[j - zj[i][0] - zj[i][2]]
          )
        }
        //主件+附件2
        if (zj[i][4] > 0 && j >= zj[i][0] + zj[i][4]) {
          dp[j] = Math.max(
            dp[j],
            zj[i][1] + zj[i][5] + dp[j - zj[i][0] - zj[i][4]]
          )
        }
        //主件+附件1+附件2
        if (zj[i][4] > 0 && j >= zj[i][0] + zj[i][2] + zj[i][4]) {
          dp[j] = Math.max(
            dp[j],
            zj[i][1] + zj[i][3] + zj[i][5] + dp[j - zj[i][0] - zj[i][2] - zj[i][4]]
          )
        }
      }
    }
  }
  //打印当接收钱数为N时的满意度
  console.log(dp[N])
})()

下图为以示例2来分析问题的部分解题思路,代码中的表格2、表格3请参考下图内容。
【华为机试刷题笔记】HJ16-购物单_第2张图片
文章系原创,在阅读过程中如若有误,劳请指正;如若有妙解、疑惑也欢迎大家和我交流,感谢!

你可能感兴趣的:(快乐刷题每一天之华为机试103,javascript,前端,算法,node.js)