价格优惠计算使用单调栈求解 | 豆包MarsCode AI 刷题

143 阅读6分钟

价格优惠计算问题

问题描述

小F在“双十一”期间购买了NN件商品。每件商品有一个价格p[i]p[i],小F可以获得的优惠取决于该商品之前的一件商品。如果某一件商品的价格p[i]p[i]大于等于前面的某个商品p[j]p[j],则小F可以享受该商品p[j]p[j]的价格作为优惠,前提是p[j]p[j]是离p[i]p[i]最近的且满足条件的商品。

例如,给定价格数组p = [9, 4, 5, 2, 4],其中p[3] = 2之前没有商品的价格小于等于p[3],因此没有优惠;而p[2] = 5可以享受最近的商品p[1] = 4的价格作为优惠。因此,任务是计算小F能获得的总优惠。


测试样例

样例1:

输入:N = 5 ,p = [9, 4, 5, 2, 4] 输出:6

样例2:

输入:N = 4 ,p = [1, 2, 3, 5] 输出:6

样例3:

输入:N = 4 ,p = [4, 3, 2, 1] 输出:0

答案

 #include <iostream>
 #include <vector>
 #include <stack>
 ​
 using namespace std;
 ​
 // 计算总优惠值的函数
 int solution(int N, vector<int>& p) {
     int totalDiscount = 0;  // 初始化总优惠值
     stack<int> s;           // 单调递增栈,用于存储价格的索引
 ​
     // 遍历价格数组
     for (int i = 0; i < N; i++) {
         // 保持栈的单调性:移除栈中价格大于当前价格的索引
         while (!s.empty() && p[s.top()] > p[i]) {
             s.pop();
         }
 ​
         // 如果栈不为空,则栈顶元素的价格是当前商品的优惠
         if (!s.empty()) {
             totalDiscount += p[s.top()];  // 累加优惠
         }
 ​
         // 将当前商品索引压入栈
         s.push(i);
     }
 ​
     return totalDiscount;  // 返回总优惠值
 }
 ​
 int main() {
     // 测试用例 1
     vector<int> p1 = {9, 4, 5, 2, 4};
     cout << "Test 1 (Expected 6): " << (solution(5, p1) == 6) << endl;
 ​
     // 测试用例 2
     vector<int> p2 = {1, 2, 3, 5};
     cout << "Test 2 (Expected 6): " << (solution(4, p2) == 6) << endl;
 ​
     // 测试用例 3
     vector<int> p3 = {4, 3, 2, 1};
     cout << "Test 3 (Expected 0): " << (solution(4, p3) == 0) << endl;
 ​
     return 0;
 }

解题思路

问题是通过找到每件商品的最近价格优惠来计算总优惠。

1. 问题转化

  • 对于每件商品的价格 p[i]

    • 找出其左侧(之前的商品中)最近且小于等于 p[i] 的价格。
    • 如果找到这样的价格,记录其值为 p[j] 并将其累加到总优惠中。
    • 如果没有找到,则该商品没有优惠。

2. 直接解法的复杂度问题

  • 暴力法

    • 每次遍历价格数组时,从当前位置向左扫描,寻找最近的小于等于的价格。
    • 时间复杂度为 O(n^2),会超时。

3. 高效解法:单调栈

单调栈是一种优化的结构,用来快速解决 "最近小于等于" 或 "最近大于" 等问题。我们用单调递增栈解决此问题。

为了高效实现,需要用栈这种数据结构解决 "最近小于等于" 的问题。

  • 使用栈来维护一个递增的价格索引序列,栈顶始终是当前价格之前最近的小于等于的价格。

  • 当遇到新的价格时:

    • 如果该价格比栈顶价格小,则将栈顶弹出,继续检查。
    • 如果该价格比栈顶价格大或等于,则可以直接计算优惠。

单调栈解法

核心思想

  • 使用栈来维护一个递增的价格索引序列,栈顶始终是当前价格之前最近的小于等于的价格。

  • 当遇到新的价格时:

    • 如果该价格比栈顶价格小,则将栈顶弹出,继续检查。
    • 如果该价格比栈顶价格大或等于,则可以直接计算优惠。

具体步骤

  1. 初始化一个栈 s,栈中存储商品的索引。

  2. 遍历价格数组 p

    • 移除不符合条件的元素

      • 如果栈顶元素的价格 p[s.top()] > p[i],则将栈顶索引弹出。
    • 计算优惠

      • 如果栈不为空,则栈顶元素对应的价格是当前商品的优惠,累加到总优惠中。
    • 将当前索引 i 压入栈。

  3. 返回总优惠值。

样例分析

输入:N = 5, p = [9, 4, 5, 2, 4] 栈变化过程:

  1. i=0p[0]=9,栈为空,入栈。
  2. i=1p[1]=4,栈中 9 > 4,弹出栈,栈为空,入栈。
  3. i=2p[2]=5,栈中 4 <= 5,优惠值为 4,入栈。
  4. i=3p[3]=2,栈中 5 > 2,弹出栈直到栈为空,入栈。
  5. i=4p[4]=4,栈中 2 <= 4,优惠值为 2,入栈。

总优惠值:4 + 2 = 6

单调栈

顾名思义:单调栈是一种特殊的栈结构,能够高效地解决类似「最近小于等于」、「最近大于」等问题。它通过维护栈中元素的单调性(递增或递减)来减少重复比较,从而提高计算效率。

核心思想

维护逻辑

  • 当插入新元素时,如果它打破了栈的单调性,就弹出栈顶元素,直到栈恢复单调性。
  • 这样,栈内始终只保留对当前问题有用的元素,栈顶对应最近相关的元素。

Case

找出每个元素左侧最近的小于等于的元素

输入:p = [9, 4, 5, 2, 4]

  1.  i=0, p[0]=9
    
    • 栈为空,直接入栈:s = [0]
  2.  i=1, p[1]=4
    
    • 栈顶 p[0]=9 > 4,弹出栈顶,栈为空。
    • 入栈:s = [1]
  3.  i=2, p[2]=5
    
    • 栈顶 p[1]=4 <= 5,找到最近的小于等于值 4
    • 入栈:s = [1, 2]
  4.  i=3, p[3]=2
    
    • 栈顶 p[2]=5 > 2,弹出。
    • 栈顶 p[1]=4 > 2,弹出。
    • 栈为空,入栈:s = [3]
  5.  i=4, p[4]=4
    
    • 栈顶 p[3]=2 <= 4,找到最近的小于等于值 2
    • 入栈:s = [3, 4]
 最近的小于等于值:
 p[0]=9 -> 无(-1)
 p[1]=4 -> 无(-1)
 p[2]=5 -> 4
 p[3]=2 -> 无(-1)
 p[4]=4 -> 2

总时间复杂度为O(n)

MarsCode体会

在算法编写时,MarsCode可以帮助快速理解问题、提供清晰的解题思路和优化建议。它能高效生成代码模板,快速验证逻辑,并指出潜在的优化点或边界条件遗漏,显著提升开发效率和代码质量,尤其在复杂算法设计或调试中表现尤为突出。

AI可以提供解题思路、优化建议和代码示例,帮助快速上手和验证逻辑。但同时,不能过于依赖AI,应注重独立思考和算法基础的掌握,以便在复杂问题或特殊场景下具备独立解决问题的能力,充分发挥AI与自身知识的结合优势。