LeetCode 638. 大礼包

题目 638. 大礼包

解题思路

看题第一眼,想到了贪心,细品,想到了动态规划,再细品,还是回溯吧。。。菜鸡的自我救赎,能暴力就暴力hhhhh。

1. 难点在于每个礼包可以选无数次,用动态规划不太好做

2.对于这种可以选多次的最优化问题,回溯最好想,而且数据量也不大,不会超时

回溯优化:剪枝。ans记录当前的最小花费,一旦某条支路上的money超过了ans,这条支路可以毫不犹豫的舍弃。c++代码如下:

class Solution {
public:
    int ans=0;//记录用的钱
    int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
        int n=price.size();
        int gift_num=special.size();

        for(int i=0;i<n;i++){//不买任何礼包时所花的钱,然后再用这个去和使用大礼包时比较,选出价格最低的
            ans+=price[i]*needs[i];
        }
        int money=0;
        Try(0,price, special, needs, money);
        return ans;
    }

    bool Safe(vector<int> gift,vector<int> needs){
        int n=needs.size();
        for(int i=0;i<n;i++){
            if(gift[i]>needs[i]) return false;
        }
        return true;
    }

    void Record(vector<int> gift,vector<int>& needs){
        int n=needs.size();
        for(int i=0;i<n;i++){
            needs[i]-=gift[i];
        }
        return ;
    }

    void Delete(vector<int> gift,vector<int>& needs){
        int n=needs.size();
        for(int i=0;i<n;i++){
            needs[i]+=gift[i];
        }
        return ;
    }

    bool isEnd(vector<int> needs){
        for(int i=0;i<needs.size();i++){
            if(needs[i]>0) return false;
        }
        return true;
    }

    void Try(int k, vector<int>& price,vector<vector<int>>& special, vector<int>& needs,int money)
    {
        int n=price.size();
        int gift_num=special.size();

        if(isEnd(needs)){//已经买完了
            ans=min(ans,money);
            return ;
        }
        if(money>ans)//剪枝
        {
            return ;
        }
        int flag=0;
        for(int i=0;i<gift_num+1;i++)
        {

            if(i<gift_num&&Safe(special[i],needs)){
                flag=1;
                Record(special[i],needs);
                money+=special[i][n];

                Try(k+1,price, special, needs, money);

                //回溯
                Delete(special[i],needs);
                money-=special[i][n];
            }
            if(i==gift_num){
                int m=0;
                for(int i=0;i<n;i++){
                    m+=price[i]*needs[i];
                }
                ans=min(ans,money+m);
            }
        }
        if(flag==0)//已经不能买任何一个大礼包,但是此时还有东西没买完
        {
            for(int i=0;i<n;i++){
                money+=price[i]*needs[i];
            }
            ans=min(ans,money);
            return ;
        }
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值