LeetCode638大礼包(7进制状压+完全背包 or DFS+剪枝)

本文探讨了LeetCode题目638的解决方案,采用状态压缩+完全背包算法解决购物优惠问题,通过DFS+剪枝优化搜索过程,详细介绍了算法实现步骤与核心代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接leetcode638

在这里插入图片描述

思路

  • 状态压缩+完全背包(状压DP)
    由于物品的种类不超过6种,实际上是6维的DP问题,可以用一个十进制数来存一个最大6位的7进制数种,之后就跟完全背包一样把每个礼包看成一个物品,而总的背包容量状态为tot,一个物品能放进背包当且仅当每个维度的物品数量都不超过背包容量。预处理和状态计算的复杂度: O ( 6 + 106 ∗ 7 7 ∗ ( 6 ∗ 2 ) ) O(6+106*7^7*(6*2)) O(6+10677(62))
class Solution {
    const int Decinal[7] = {1,7,49,343,2401,16807,117649};
public:

    //把第x种物品有y个的数压入7进制数中,传该7进制的10进制表示数
    inline int change1(int x, int y) {
        return y * Decinal[x - 1];
    }

    //把集合S的k倍压入7进制数中,传该7进制的10进制表示数
    inline int change2(vector<int>& S, int n) {
        int res = 0;
        for(int i=0; i<n; i++) res += (S[i] * Decinal[i]);
        return res;
    }

    //检查是否可以再卖一个礼包S
    inline bool check(vector<int>& S, vector<int>& needs, int n, int pack) {
        for(int i=0; i<n; i++) {
            if( (pack % 7) + S[i] > needs[i]) 
                return false;
            pack /= 7;
        }
        return true;
    }

    int shoppingOffers(vector<int>& price, vector<vector<int>>& special, vector<int>& needs) {
        //"只允许尽可能地不贵,不允许浪费" -- 就是购物清单的背包恰好装满的最少费用
        
        //把单件物品也封装成一个礼包方便后面进行完全背包
        for(int i=0; i<price.size(); i++) {
            vector<int> tmp(price.size()+1, 0);
            tmp[price.size()]=price[i];
            tmp[i]=1;
            special.push_back(tmp);
        }

        int box = special.size(); //礼包种类的数量
        int goods = price.size(); //物品种类的数量
        int tot = change2(needs, goods); //规模:最终要达到的状态
        vector<long long> dp(tot+1, INT_MAX); //达到最终规模所需要的最少费用,初始化为INT_MAX,因为要求最小值
        
        dp[0]=0; //0个物品的最小值为0

        for(int i=0; i<box; i++) {
            for(int j=0; j<=tot; j++) {
                if(!check(special[i], needs, goods, j)) continue; //非法卖礼盒
                int state = j + change2(special[i], goods); //卖该物品后的状态
                dp[state] = min(dp[state], dp[j]+special[i][goods]); //状态更新
            }
        }            
        return dp[tot];
    }
};
  • DFS+剪枝
class Solution {
public:
//判断是否能够放入当前这个礼包
    bool valid(vector<int> &special_single, vector<int> &needs){
        for(int i=0;i<needs.size();i++){
            if(special_single[i]>needs[i]) return false;
        }
        return true;
    }
int shoppingOffers(vector<int> &price, 
        vector<vector<int>> &special, vector<int> &needs) {
        int min_money = 0; //表示最小值
        //如果全部买单独的物品
        for(int i=0;i<needs.size();i++){
            min_money += price[i] * needs[i];
        }
        //遍历所有礼包
        for(int i=0;i<special.size();i++){
            if(valid(special[i],needs)){
                //如果礼包可以购买的话
                vector<int> new_needs; //新建一个数组 表示新的needs
                for(int j=0;j<needs.size();j++)
                    //新的needs = 旧的needs - 礼包中物品的个数
                    new_needs.push_back(needs[j]-special[i][j]); 
                 //将当前礼包的几个加到money_temp上,同时继续遍历
           int money_tmp = shoppingOffers(price,special,new_needs) +
               special[i].back();
           min_money = min(min_money,money_tmp); //最后求出最低价格
            }
        }
        return min_money;//返回最低价格
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小胡同的诗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值