大礼包(Leetcode-638)-记忆化搜索&DFS

本文介绍了一种使用记忆化搜索(DP)和DFS解决LeetCode上的购物优惠问题的方法。作者通过创建状态并定义状态转移方程来找到使总价格最低的购买方案。代码中包含了检查特殊优惠是否满足条件的函数、更新状态的函数以及深度优先搜索的实现。虽然尝试将某些代码段合并以提高效率,但仍有待进一步优化。
摘要由CSDN通过智能技术生成

题目

知识点

  • 记忆化搜索(DP)
  • DFS

思路

  • 我们把当前购买数量情况作为状态,那我们要求的就是满足题意下使得我们的价格最低,那么状态转移方程就是:
    当前状态的最小价格 = min((满足条件的情况n状态的最小价格)+ 情况n的价格))
  • 上述转移方程中的满足条件情况分两种:
    1. 有special大礼包可以选择,即大礼包中的每一项数量都小于目前需要的数量(注意:是当前需要的数量,Line 9: choice[i] > ed[i] - status[i]),写了一个 check() 进行判断
    2. 直接不选大礼包,计算当前状态单买的价格,因为在测试数据里有单买比大礼包还便宜的情况!(就离谱…),因此情况1递归返回以后的结果取最小以后,还要和这个情况比较一下,选择最小的返回当前状态的结果,并保存在 map<vector<int>, int> m 中(这个状态用的 vector 保存,所以我直接用了一个 vector->intmap)。

代码

  • Line 45 - 51 行的代码照理来说可以与 Line 37-41 合并,但是合并以后有一组数据一直过不了QAQ,整体的时间效率有待提高(当然比不用记忆化搜索(dp)方式还是快上不少的)
class Solution {
public:
    map<vector<int>, int> m;
    vector<int> p, ed;  // price & need
    vector<vector<int>> sp;  // special

    bool check(vector<int> status, vector<int> choice) { // 检查special是否满足条件
        for (int i = 0; i < status.size(); i++) {
            if (choice[i] > ed[i] - status[i]) {  // special包含数目大于需求数
                return false;
            }
        }
        return true;
    }

    vector<int> update(vector<int> status, vector<int> choice) {
        for (int i = 0; i < status.size(); ++i) {
            status[i] += choice[i];
        }
        return status;
    }

    int dfs(const vector<int> &status, int current) {  // 状态 & 对应的花费
        if (m.count(status)) return m[status];  // 当前状态遍历过

        // 当前状态的最小价格 = min((满足条件的情况n状态的最小价格)+ 情况n的价格))
        int ans = 1e9;
        bool is_ok = false;
        // special情况
        for (const vector<int> &choice:sp) {
            if (check(status, choice)) {
                int price = choice[choice.size() - 1];
                is_ok = true;
                vector<int> status_new = update(status, choice);
                ans = min(ans, dfs(status_new, current + price) + price);
                // 新状态直接单买是否更便宜?
                int sum = 0;
                for (int i = 0; i < status_new.size(); i++) {
                    sum += (ed[i] - status_new[i]) * p[i];
                }
                ans = min(ans, current + price + sum);
            }
        }

        if (!is_ok) {  // 如果没有选special,直接计算差了多少,买单个的,返回计算的值,递归结束
            int sum = 0;
            for (int i = 0; i < status.size(); i++) {
                sum += (ed[i] - status[i]) * p[i];
            }
            return sum;
        }

        m[status] = ans;
        return ans;
    }

    int shoppingOffers(vector<int> &price, vector<vector<int>> &special, vector<int> &needs) {
        p = price, ed = needs, sp = special;
        int direct = 0;
        for (int i = 0; i < ed.size(); i++) {
            direct += (ed[i]) * p[i];
        }
        return min(direct, dfs(vector<int>(needs.size(), 0), 0));  // 初始状态,全部为0,还没有买任何东西;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值