【中等】算法nodeJs:购物单

122 阅读3分钟

王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件。
主件可以没有附件,至多有 2 个附件。附件不再有从属于自己的附件。如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。

王强查到了每件物品的价格,而他只有 n 元的预算。为了先购买重要的物品,他给每件物品规定了一个重要度,用整数 1∼5 表示。他希望在花费不超过 n 元的前提下,使自己的满意度达到最大。
满意度是指所购买的每件物品的价格与重要度的乘积的之和,具体地说,记第 i 件物品的价格为 vi​,重要度为 wi​;现在,一共选中了 k 件物品,编号依次为 j1,j2,…,jk,则满意度计算为:

image.png

请你帮助王强计算可获得的最大的满意度。

输入描述:

第一行输入两个整数 n,m(1≦n≦32000,1≦m≦60) 代表预算、需要购买的物品总数。
此后 m 行,第 i 行输入三个整数 vi,wi,qi(1≦vi≦104; 1≦wi≦5; 0≦qi≦m) 代表第 i 件物品的价格、重要度、主件编号。特别地,qi=0 代表该物品为主件。编号即输入顺序,从 1 开始。

特别地,保证全部物品的价格 v 均为 10 的倍数。

输出描述:

在一行上输出一个整数,代表王强可获得的最大满意度。

  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 inputs = []; //保存输入内容
    // 每一次有新的输入都会触发异步函数readline
    while ((line = await readline())) {
        inputs.push(line.split(" ").map((n) => parseInt(n)));
    }
    let n = 0, //声明预算
        m = 0; //声明物品总数
    let primary = {}, //主件价格、重要度
        annex = {}; //附件价格、重要度
    n = inputs[0][0];
    m = inputs[0][1];
    for (let i = 1; i < m + 1; i++) {
        let x, y, z;
        [x, y, z] = inputs[i];
        if (z === 0) {
            // 主件
            primary[i] = [x, y];
        } else {
            // 附件
            if (annex[z]) {
                // 已经有记录,添加附件,没有记录,新增附件列表
                annex[z].push([x, y]);
            } else {
                annex[z] = [[x, y]];
            }
        }
    }

    m = Object.keys(primary).length; //数量转为主件数量  Object.keys()方法返回一个由key组成的数组
    dp = []; //生成一个m+1 * n+1的二维数组 用来存放最大满意度
    // 下面遍历相当于const dp = Array.from({length:m+1},()=>Array(n+1).fill(0))
    for (let i = 0; i < m + 1; i++) {
        let temp = [];
        for (let j = 0; j < n + 1; j++) {
            temp.push(0);
        }
        dp.push(temp);
    }

    let w = [[]], //存每一个主件包括附带附件的价格
        v = [[]]; //存每一个主件包括附带附件的满意度

    for (let key in primary) {
        let w_temp = [],//存每一次的价格
            v_temp = [];//存每一次的满意度
        w_temp.push(primary[key][0]); //主件价格
        v_temp.push(primary[key][0] * primary[key][1]); //满意度
        if (annex[key]) {
            w_temp.push(w_temp[0] + annex[key][0][0]); //主件+附件1
            v_temp.push(v_temp[0] + annex[key][0][0] * annex[key][0][1]);
            if (annex[key].length === 2) {
                //2个附件
                w_temp.push(w_temp[0] + annex[key][1][0]); //主件+附件2
                v_temp.push(v_temp[0] + annex[key][1][0] * annex[key][1][1]);
                w_temp.push(w_temp[0] + annex[key][0][0] + annex[key][1][0]); //主件+附件1+附件2
                v_temp.push(
                    v_temp[0] +
                        annex[key][0][0] * annex[key][0][1] +
                        annex[key][1][0] * annex[key][1][1]
                );
            }
        }
        w.push(w_temp);
        v.push(v_temp);
    }
    //动态规划
    for (let i = 1; i < m + 1; i++) {
        for (let j = 10; j < n + 1; j += 10) {
            //先保存上一格的满意度
            let max_i = dp[i - 1][j];
            //遍历主件加附件的不同情况的价格
            for (let k = 0; k < w[i].length; k++) {
                //满足预算
                //j - w[i][k]其实就是对应的预算列
                if (j - w[i][k] >= 0) {
                    //上一个单元格的价值 VS 当前的商品价值+剩余空间的价值
                    max_i = Math.max(max_i, dp[i - 1][j - w[i][k]] + v[i][k]);
                }
            }
            dp[i][j] = max_i;
        }
    }
    console.log(dp[m][n]);
})();