价格优惠计算问题 | 豆包MarsCode AI 刷题

121 阅读6分钟

题目链接 价格优惠计算问题 - 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)来优化查找过程。通过栈来维护当前商品价格的顺序,能够让我们在遍历时高效地找到满足条件的 最近的价格小于等于当前商品的价格

单调栈的工作原理

  1. 栈的作用

    • 我们维护一个栈,栈中存储的是商品价格的下标(而不是价格本身)。栈内的商品价格是 单调递减的,即栈顶的商品价格比栈底的商品价格要小或者相等。
    • 这样,当我们需要找到价格小于等于当前商品的最近价格时,栈顶的商品就是符合条件的商品。
  2. 栈的操作

    • 我们从左到右遍历商品,对于每个商品 p[i],我们查看栈顶元素,判断栈顶商品的价格是否小于等于 p[i]:

      • 如果栈顶商品价格大于 p[i],则弹出栈顶,直到找到符合条件的商品。
      • 如果栈顶商品价格小于等于 p[i],则栈顶商品的价格就是当前商品的优惠价格。
    • 无论找到优惠与否,当前商品的下标都会被压入栈中,供后续商品进行查询。

  3. 复杂度分析

    • 每个商品最多会入栈和出栈一次,因此栈的操作是 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