价格优惠计算问题
问题描述
小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. 高效解法:单调栈
单调栈是一种优化的结构,用来快速解决 "最近小于等于" 或 "最近大于" 等问题。我们用单调递增栈解决此问题。
为了高效实现,需要用栈这种数据结构解决 "最近小于等于" 的问题。
-
使用栈来维护一个递增的价格索引序列,栈顶始终是当前价格之前最近的小于等于的价格。
-
当遇到新的价格时:
- 如果该价格比栈顶价格小,则将栈顶弹出,继续检查。
- 如果该价格比栈顶价格大或等于,则可以直接计算优惠。
单调栈解法
核心思想
-
使用栈来维护一个递增的价格索引序列,栈顶始终是当前价格之前最近的小于等于的价格。
-
当遇到新的价格时:
- 如果该价格比栈顶价格小,则将栈顶弹出,继续检查。
- 如果该价格比栈顶价格大或等于,则可以直接计算优惠。
具体步骤
-
初始化一个栈
s,栈中存储商品的索引。 -
遍历价格数组
p-
移除不符合条件的元素
- 如果栈顶元素的价格
p[s.top()] > p[i],则将栈顶索引弹出。
- 如果栈顶元素的价格
-
计算优惠
- 如果栈不为空,则栈顶元素对应的价格是当前商品的优惠,累加到总优惠中。
-
将当前索引
i压入栈。
-
-
返回总优惠值。
样例分析
输入:N = 5, p = [9, 4, 5, 2, 4] 栈变化过程:
i=0:p[0]=9,栈为空,入栈。i=1:p[1]=4,栈中9 > 4,弹出栈,栈为空,入栈。i=2:p[2]=5,栈中4 <= 5,优惠值为4,入栈。i=3:p[3]=2,栈中5 > 2,弹出栈直到栈为空,入栈。i=4:p[4]=4,栈中2 <= 4,优惠值为2,入栈。
总优惠值:4 + 2 = 6。
单调栈
顾名思义:单调栈是一种特殊的栈结构,能够高效地解决类似「最近小于等于」、「最近大于」等问题。它通过维护栈中元素的单调性(递增或递减)来减少重复比较,从而提高计算效率。
核心思想
维护逻辑:
- 当插入新元素时,如果它打破了栈的单调性,就弹出栈顶元素,直到栈恢复单调性。
- 这样,栈内始终只保留对当前问题有用的元素,栈顶对应最近相关的元素。
Case
找出每个元素左侧最近的小于等于的元素
输入:p = [9, 4, 5, 2, 4]
-
i=0, p[0]=9- 栈为空,直接入栈:
s = [0]。
- 栈为空,直接入栈:
-
i=1, p[1]=4- 栈顶
p[0]=9 > 4,弹出栈顶,栈为空。 - 入栈:
s = [1]。
- 栈顶
-
i=2, p[2]=5- 栈顶
p[1]=4 <= 5,找到最近的小于等于值4。 - 入栈:
s = [1, 2]。
- 栈顶
-
i=3, p[3]=2- 栈顶
p[2]=5 > 2,弹出。 - 栈顶
p[1]=4 > 2,弹出。 - 栈为空,入栈:
s = [3]。
- 栈顶
-
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与自身知识的结合优势。