题目链接 价格优惠计算问题 - MarsCode
问题描述:
小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
思路分析:
给定一个商品价格数组 p=[p[1],p[2],...,p[N]],对于每个商品 p[i],我们要找到它前面所有商品中 最后一个价格小于等于 p[i] 的商品 p[j](其中 j<ij<i),并将该商品的价格 p[j] 作为优惠价格。如果没有这样的商品 p[j],则该商品无法享受优惠,优惠为 0。
实现方法:
1. 暴力解法:
对于每个商品 p[i],我们可以通过遍历其前面所有的商品 p[j] 来找到符合条件的商品。这种方法的时间复杂度是 O(NN),因为对于每个商品,我们最多需要查找 O(N)次,导致总时间复杂度是 O(NN),在商品数量很大的时候,效率会非常低。
1. 使用单调栈:
为了提高效率,我们可以使用 单调栈(Monotonic Stack)来优化查找过程。通过栈来维护当前商品价格的顺序,能够让我们在遍历时高效地找到满足条件的 最近的价格小于等于当前商品的价格。
单调栈的工作原理
-
栈的作用:
- 我们维护一个栈,栈中存储的是商品价格的下标(而不是价格本身)。栈内的商品价格是 单调递减的,即栈顶的商品价格比栈底的商品价格要小或者相等。
- 这样,当我们需要找到价格小于等于当前商品的最近价格时,栈顶的商品就是符合条件的商品。
-
栈的操作:
-
我们从左到右遍历商品,对于每个商品 p[i],我们查看栈顶元素,判断栈顶商品的价格是否小于等于 p[i]:
- 如果栈顶商品价格大于 p[i],则弹出栈顶,直到找到符合条件的商品。
- 如果栈顶商品价格小于等于 p[i],则栈顶商品的价格就是当前商品的优惠价格。
-
无论找到优惠与否,当前商品的下标都会被压入栈中,供后续商品进行查询。
-
-
复杂度分析:
- 每个商品最多会入栈和出栈一次,因此栈的操作是 O(1) 的。
- 整体的时间复杂度是 O(N) ,比暴力解法的时间复杂度要高效得多。
具体实现:
1.初始化变量:
totalDiscount:用来存储所有商品的优惠总和stack:栈中存储商品的下标,栈内的商品价格是递减的。
int totalDiscount = 0; // 结果变量,用来保存所有商品的优惠总和
Stack<Integer> stack = new Stack<>(); // 创建一个栈,用来存储商品的下标
3.遍历每个商品:
-
对于每个商品 p[i],我们通过栈来找出符合条件的商品:
- 如果栈顶的商品价格大于当前商品的价格 p[i],我们就弹出栈顶元素,直到栈顶商品的价格小于等于当前商品的价格。
- 如果栈不为空,栈顶商品的价格就是当前商品的优惠价格。
4.入栈操作:
- 每次处理完当前商品后,将当前商品的下标压入栈中,作为后续商品的参考。
// 遍历所有商品
for (int i = 0; i < N; i++) {
// 当栈不为空且栈顶商品的价格大于当前商品的价格时,弹出栈顶元素
while (!stack.isEmpty() && p[stack.peek()] > p[i]) {
stack.pop();
}
// 如果栈不为空,说明栈顶的商品是符合条件的商品
if (!stack.isEmpty() && p[stack.peek()] <= p[i]) {
// 将栈顶商品的价格作为当前商品的优惠
totalDiscount += p[stack.peek()];
}
// 将当前商品的下标压入栈中
stack.push(i);
}
返回结果:
- 遍历完成后,返回
totalDiscount,即所有商品的优惠总和。
//返回最终的优惠总和
return totalDiscount;
完整代码:
import java.util.Stack;
public class Main {
public static int solution(int N, int[] p) {
// 结果变量,用来保存所有商品的优惠总和
int totalDiscount = 0;
// 创建一个栈,用来存储商品的下标
Stack<Integer> stack = new Stack<>();
// 遍历所有商品
for (int i = 0; i < N; i++) {
// 当栈不为空且栈顶商品的价格大于当前商品的价格时,弹出栈顶元素
while (!stack.isEmpty() && p[stack.peek()] > p[i]) {
stack.pop();
}
// 如果栈不为空,说明栈顶的商品是符合条件的商品
if (!stack.isEmpty() && p[stack.peek()] <= p[i]) {
// 将栈顶商品的价格作为当前商品的优惠
totalDiscount += p[stack.peek()];
}
// 将当前商品的下标压入栈中
stack.push(i);
}
// 返回最终的优惠总和
return totalDiscount;
}
public static void main(String[] args) {
// 测试样例1
System.out.println(solution(5, new int[]{9, 4, 5, 2, 4}) == 6); // 输出: 6
// 测试样例2
System.out.println(solution(4, new int[]{1, 2, 3, 5}) == 6); // 输出: 6
// 测试样例3
System.out.println(solution(4, new int[]{4, 3, 2, 1}) == 0); // 输出: 0
}
}
测试样例分析
示例1:
输入:
java
N = 5, p = [9, 4, 5, 2, 4]
- 处理商品 1:9,没有优惠,栈为空。
- 处理商品 2:4,没有优惠,栈为空。
- 处理商品 3:5,栈顶商品是 4,优惠为 4。
- 处理商品 4:2,没有优惠,栈为空。
- 处理商品 5:4,栈顶商品是 4,优惠为 4。
总优惠:4 + 2 = 6。
输出:
java
6
示例2:
输入:
java
N = 4, p = [1, 2, 3, 5]
- 处理商品 1:1,没有优惠。
- 处理商品 2:2,栈顶商品是 1,优惠为 1。
- 处理商品 3:3,栈顶商品是 2,优惠为 2。
- 处理商品 4:5,栈顶商品是 3,优惠为 3。
总优惠:1 + 2 + 3 = 6。
输出:
java
6
示例3:
输入:
java
N = 4, p = [4, 3, 2, 1]
所有商品的价格都比前面的商品便宜,因此没有任何商品能够享受优惠。
输出:
0