leetcode 大礼包

题目链接
思路一:dfs+剪枝
剪枝的思路就是:1.购买整个礼包花的钱比单买便宜 2.购买整个礼包不会使得商品数大于对应的所需商品数

class Solution {

    private int min = Integer.MAX_VALUE;
    private List<Integer> price;
    private List<List<Integer>> special;
    //记录某个礼包是否有优惠
    boolean[] discount;

    public int shoppingOffers(List<Integer> _price, List<List<Integer>> _special, List<Integer> needs) {
        this.price = _price;
        this.special = _special;
        discount = new boolean[_special.size()];

        //剪枝的准备工作
        for(int i = 0; i<_special.size();i++){
            List<Integer> singleSpecial = _special.get(i);
            int spend = 0;
            for(int j = 0; j<singleSpecial.size();j++){
                if(j == singleSpecial.size()-1 ){
                    if(spend > singleSpecial.get(j)){
                        //有优惠
                        discount[i] = true;
                    }
                }else{
                    //计算单个买要花的钱
                    spend += (singleSpecial.get(j) * _price.get(j));
                }
            }
        }
        dfs(needs,0);
        return min;
    }

    public void dfs(List<Integer> needs,int money){
    	if(money>min){
            return;
        }
        //尝试每一种大礼包,看看哪一种符合就用哪一种
        for(int i = 0; i < special.size(); ++i){
            if(!discount[i]){
                continue;
            }
            //取出第i个礼包
            List<Integer> list = special.get(i);

            //用来记录下一层需要的商品个数
            List<Integer> cur = new ArrayList<>();

            //循环礼包中的每个商品的数量
            for(int j = 0; j < list.size(); ++j){
                if(j==list.size()-1){
                    //购买了第i个礼包,进入下一层
                    dfs(cur,money+list.get(j));
                } else {
                    //num记录第j个礼包所需要的数量
                    int num = needs.get(j);
                    //当所需商品的数量小于了这个礼包中这个商品的数量直接不能买这个礼包
                    if(num<list.get(j)) break;
                    //否则,所需的减少数量
                    cur.add(num-list.get(j));
                }
            }
        }
        //走到这里说明没有符合的大礼包了,直接减去每个的单价就行
        int m = money;
        for(int i = 0; i < needs.size(); ++i){
            m += needs.get(i) * price.get(i);
        }
        min = Math.min(min,m);
    }
}

思路二:通过数组表示某个礼包买多少个,然后枚举每个礼包从0–10个,然后剪枝,也是dfs+剪枝,不过角度不一样。

class Solution {

    private int min = Integer.MAX_VALUE;
    private List<Integer> price;
    private List<List<Integer>> special;
    boolean[] discount;

    public int shoppingOffers(List<Integer> _price, List<List<Integer>> _special, List<Integer> needs) {
        this.price = _price;
        this.special = _special;
        discount = new boolean[_special.size()];

        //剪枝的准备工作
        for(int i = 0; i<_special.size();i++){
            List<Integer> singleSpecial = _special.get(i);
            int spend = 0;
            for(int j = 0; j<singleSpecial.size();j++){
                if(j == singleSpecial.size()-1 ){
                    if(spend > singleSpecial.get(j)){
                        //有优惠
                        discount[i] = true;
                    }
                }else{
                    //计算单个买要花的钱
                    spend += (singleSpecial.get(j) * _price.get(j));
                }
            }
        }
        dfsII(needs,0,0);
        return min;
    }
    
	 /**
     *
     * @param needs
     * @param money
     * @param curIndex 当前礼包的下标
     */
    public void dfsII(List<Integer> needs,int money,int curIndex){
        if(money > min){
            return;
        }
        //没有越界
        if(curIndex<special.size()){
            boolean overflow = false;
            //购买第curIndex礼包count个
            for(int count = 0; count < 10;count++){
                if(!discount[curIndex]){
                    dfsII(needs,money,curIndex+1);
                    break;
                }
                //取出第curIndex个礼包
                List<Integer> list = special.get(curIndex);

                //用来记录下一层需要的商品个数
                List<Integer> cur = new ArrayList<>();

                //循环礼包中的每个商品的数量
                for(int j = 0; j < list.size(); ++j){
                    if(j==list.size()-1){
                        //购买了第curIndex个i个礼包,进入下一层
                        dfsII(cur,money+list.get(j)*count,curIndex+1);
                    } else {
                        //num记录第j个商品所需要的数量
                        int num = needs.get(j);
                        //当所需商品的数量小于了这个礼包中这个商品的数量直接不能买这个礼包
                        if(num<list.get(j)*count) {
                            overflow = true;
                            break;
                        }
                        //否则,所需的减少数量
                        cur.add(num-list.get(j)*count);
                    }
                }
                if(overflow){
                    //表示这个礼包不能买count个了,只能小于count个
                    break;
                }
            }
        }
        if(min<=money){
            return;
        }
        for(int i = 0; i < needs.size();i++){
            money += (needs.get(i) * price.get(i));
        }
        min = Math.min(min,money);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值