最少代金券问题之JavaScript解法

前言

最近在牛客网上刷题,遇到一题代金券的问题,花了我一下午时间研究(我才刚开始复习算法和数据结构),写此文章来记录一下。

题目传送门:https://www.nowcoder.com/question/next?pid=21910781&qid=894518&tid=31674041 第六题

 

问题

近期某商场由于周年庆,开启了“0元购”活动。活动中,消费者可以通过组合手中的代金券,实现0元购买指定商品。

聪明的小团想要用算法来帮助他快速计算:对于指定价格的商品,使用代金券凑出其价格即可,但所使用的代金券总面额不可超过商品价格。由于代金券数量有限,使用较少的代金券张数则可以实现价值最大化,即最佳优惠。

假设现有100元的商品,而代金券有50元、30元、20元、5元四种,则最佳优惠是两张50元面额的代金券;而如果现有65元的商品,则最佳优惠是两张30元代金券以及一张5元代金券。

请你帮助小团使用一段代码来实现代金券计算。

 

思考

蠢蠢的我第一反应就是使用贪心算法(毕竟我还没开始复习),但是后来发现通过率不高才想起贪心算法算出最优解是有条件的,这题并不满足。(贪心算法是啥?搜其他文章去吧)所以要用动态规划(看了很多其他文章才发现的,其实这类问题叫做找零问题)。有很多解法都是用其他语言的,然后尤为大佬是用js写的,但是没有任何注释解释,看得我一脸懵逼。

然后我就通过其他语言弄懂了流程后,再结合JS完成了代码

 

代码

var target_money = 3;
var price_list = [2, 5, 7, 20, 50];
console.log(fn(target_money, price_list));

function fn(tg, pl) {
    var dp = [];
    // 初始化矩阵,令每种钱解决0元的都为0张
    // 初始化顶栏
    for (let i = 0; i < pl.length; i++) {
        dp.push([0])
    }
    // 初始化左栏
    for (let i = 1; i <= tg; i++) {
        if (i % pl[0] == 0) dp[0][i] = parseInt(i / pl[0]);
        // pl[0]为最小的,除不尽用无限大
        else dp[0][i] = Infinity;
    }
    // 开始绘制整张表
    for (let i = 1; i <= tg; i++) {
        for (let j = 1; j < pl.length; j++) {
            // 用不到pl[j]金额的
            if (i < pl[j]) dp[j][i] = dp[j - 1][i];
            // 金额相等一张就够了
            else if (i == pl[j]) dp[j][i] = 1;
            else {
                // 左边的
                var left = dp[j - 1][i];
                // 上方的
                var up = dp[j][i - pl[j]] + 1;
                // 取较小的存入
                dp[j][i] = left < up ? left : up;
            }
        }
    }
    var res = dp[pl.length - 1][tg]
    // console.log(dp)
    return res == Infinity ? "Impossible" : res;
}

 

解释

动态规划是要创建一张表,dp是一个二维数组,横向表示代金券的种类,纵向代表需要组成的金额数。用 dp(j,i)来表示使用前j种代金券来构成i元的所需的代金券数量。横向的代金券金额需要按照小到大的顺序排列,纵向是0到目标金额。

dp(j , i)的值等于dp(j-1,i)和dp(j,i - pl[j]) + 1 中小的一个,前一个代表不用金额种类j的代金券来解决金额i的,最少张数,后一个代表金额i减掉当前代金券金额后,用当前金额的代金券解决的最少张数。因为减掉了当前代金券金额,相当于使用了一张当前代金券,所以后面要加一。

初始化:用前j种代价券来解决0元的情况,结果都为0张。用第0种代金券来处理0到tg金额只有两种情况,可以除得尽,那就填入张数。除不尽的话,因为题目要求所以置为js中的无穷大Infinity。相当于dp表的最左边和最上边都有值了,后面的数都是比较左边一位和上边的数。

 

结束

才发现前端也要学好算法,好好加油吧。

你可能感兴趣的:(最少代金券问题之JavaScript解法)