leetcode刷题 638大礼包

在 LeetCode 商店中, 有 n 件在售的物品。每件物品都有对应的价格。然而,也有一些大礼包,每个大礼包以优惠的价格捆绑销售一组物品。

给你一个整数数组 price 表示物品价格,其中 price[i] 是第 i 件物品的价格。另有一个整数数组 needs 表示购物清单,其中 needs[i] 是需要购买第 i 件物品的数量。

还有一个数组 special 表示大礼包,special[i] 的长度为 n + 1 ,其中 special[i][j] 表示第 i 个大礼包中内含第 j 件物品的数量,且 special[i][n] (也就是数组中的最后一个整数)为第 i 个大礼包的价格。

返回确切满足足购物清单所需花费的最低价格,你可以充分利用大礼包的优惠活动。你不能购买超出购物清单指定数量的物品,即使那样会降低整体价格。任意大礼包可无限次购买。

解题思路:

        由于购买礼包一定比单个购买要划算,因此要想所花金额最少,一定要尽量购买礼包。同时题目还要求不能让购买的商品总数超过需求量,这减小了题目的难度。本题可以使用回溯思想,将所有尽可能买礼包的情况都计算一遍,比较得所花的最少金额。

        假设一共有n个礼包,编号为:0,1,2……n-1,则思路为:

        1.先根据商品集合needs,购买尽可能多的0号礼包(假设购买量为quantity[0]),并更新needs;之后根据更新后的needs购买尽可能多的1号礼包(假设购买量为quantity[1]),并更新needs……一直到最后尽可能多的把n-1号礼包都购买好后,再把needs剩余的部分(这些部分无法再用礼包购买)单独购买,算出一次的金额。

        2.之后回溯到购买n-2号商品的情况,将n-2后商品的购买量减去1(此时购买数量为quantity[n-2]=quantity[n-2]-1),之后再尽可能多的购买n-1号商品又可以算出一次金额。之后再让n-2号商品的购买量再次减1(quantity[n-2]=quantity[n-2]-1),就这样直到n-2号商品的购买量到0位置。

        3.接下来回溯到n-3号商品的,与2类似,每次令quantity[n-3]=quantity[n-3]-1,然后尽可能多的购买n-2号和n-1号商品(n-3号商品购买量每减少1,就要重复一次第2步考察所有尽可能多的购买n-2号商品和n-1号商品的情况)。之后再不断回溯到低n-4,n-5直到0号商品,这样就可以比较得出所有金额中最小的数值。

        代码如下:

   int res = Integer.MAX_VALUE;

    public int shoppingOffers(List<Integer> price, List<List<Integer>> special, List<Integer> needs) {
        //优先使用大礼包
        getRes(price, special, needs, 0, 0);//从第0号礼包开始使用
        return res;
    }

    /**
     * @param price
     * @param special
     * @param needs
     * @param specialNo 代表现在需要使用的礼包的下标
     * @param total     至今为止总共花费的钱
     */
    private void getRes(List<Integer> price, List<List<Integer>> special, List<Integer> needs, int specialNo, int total) {
        List<Integer> specialPack = special.get(specialNo);
        int specialQuantity = getSpecialQuantity(special, needs, specialNo);//根据needs获取specialNo编号的大礼包最多使用几个
        for (int i = specialQuantity; i >= 0; i--) {
            int newTotal = total;
            ArrayList<Integer> newNeeds = new ArrayList<>(needs);//复制一份needs
            updataNewNeeds(newNeeds, i, specialPack);//使用specialNo号的礼包后,更新newNeeds
            newTotal += i * specialPack.get(specialPack.size() - 1);//更新total的金额数

            if (specialNo + 1 <= special.size() - 1) getRes(price, special, newNeeds, specialNo + 1, newTotal);
            if (specialNo == special.size() - 1) {//此时已经将最后一个礼包使用结束了,剩余部分就应该单个购买
                int sum = newTotal + getSumFromPrice(price, newNeeds);
                res = Math.min(res, sum);
            }
        }


    }

    private void updataNewNeeds(ArrayList<Integer> newNeeds, int speciaQuantity, List<Integer> specialPack) {
        for (int i = 0; i < newNeeds.size(); i++) {
            newNeeds.set(i, newNeeds.get(i) - speciaQuantity * specialPack.get(i));
        }
    }

    //获取specialNo编号的大礼包最多可以使用几个
    private int getSpecialQuantity(List<List<Integer>> special, List<Integer> newNeeds, int specialNo) {
        int quantity = Integer.MAX_VALUE;
        List<Integer> specialPack = special.get(specialNo);//通过礼包编号确定礼包
        for (int i = 0; i < newNeeds.size(); i++) {
            if (specialPack.get(i) != 0) {
                int temp = newNeeds.get(i) / specialPack.get(i);
                if (temp == 0) return 0;//代表礼包中的商品大于所需商品,不能买这个礼包
                quantity = Math.min(temp, quantity);
            }
        }
        if (quantity == Integer.MAX_VALUE) quantity = 0;
        return quantity;
    }


    //不能使用大礼包后,只能从price中计算金额
    private int getSumFromPrice(List<Integer> price, List<Integer> needs) {
        int sum = 0;
        for (int i = 0; i < price.size(); i++) {
            sum += needs.get(i) * price.get(i);
        }
        return sum;
    }

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值