价格优惠计算问题

61 阅读4分钟

做题笔记

问题描述

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

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

任务是计算小F能获得的总优惠。

思路分析

  1. 问题分析:

    • 我们需要遍历价格数组 p[i]p[i]p[i],并找出每个商品之前的最近的满足条件的商品 p[j]p[j]p[j],即 p[j]≤p[i]p[j] \leq p[i]p[j]≤p[i]。
    • 如果找到了满足条件的商品,我们就将该商品的价格作为优惠,并累加到总优惠中。
    • 需要注意的是,每个商品只能享受一个优惠,这个优惠是基于其前面最近符合条件的商品。
  2. 优化思路:

    • 直接遍历每个商品,对于每个商品 p[i]p[i]p[i],从 i−1i-1i−1 到 0 依次查找符合条件的商品会有 O(N2)O(N^2)O(N2) 的时间复杂度,这在商品数量较大时可能会很慢。

    • 可以利用 来优化这一过程。栈可以帮助我们保持商品的价格及其索引,快速找到每个商品的最近满足条件的商品。

    • 具体做法是:

      • 从左到右遍历每个商品,对于当前商品 p[i]p[i]p[i],我们使用栈来存储当前价格能够享受优惠的商品。
      • 如果栈顶的商品价格小于等于当前商品 p[i]p[i]p[i],则栈顶的商品可以作为当前商品的优惠。
      • 否则,弹出栈顶商品,直到找到一个符合条件的商品。
      • 每找到一个符合条件的商品后,就将其价格加入总优惠。
  3. 时间复杂度分析:

    • 使用栈优化后,每个商品最多进栈和出栈一次,因此时间复杂度为 O(N)O(N)O(N),比直接双重遍历的 O(N2)O(N^2)O(N2) 高效得多。

Java代码实现

import java.util.Stack;

public class ShoppingDiscount {
    
    public static int calculateTotalDiscount(int N, int[] p) {
        // 初始化一个栈,存储商品的价格
        Stack<Integer> stack = new Stack<>();
        int totalDiscount = 0;
        
        for (int i = 0; i < N; i++) {
            // 如果栈非空并且栈顶的商品价格大于当前商品价格
            // 就把栈顶的商品价格作为优惠价格
            while (!stack.isEmpty() && stack.peek() > p[i]) {
                stack.pop();
            }
            
            if (!stack.isEmpty()) {
                totalDiscount += stack.peek();
            }
            
            // 将当前商品价格压入栈中
            stack.push(p[i]);
        }
        
        return totalDiscount;
    }

    public static void main(String[] args) {
        // 示例1
        int[] p1 = {9, 4, 5, 2, 4};
        System.out.println(calculateTotalDiscount(5, p1)); // 输出:6

        // 示例2
        int[] p2 = {1, 2, 3, 5};
        System.out.println(calculateTotalDiscount(4, p2)); // 输出:6

        // 示例3
        int[] p3 = {4, 3, 2, 1};
        System.out.println(calculateTotalDiscount(4, p3)); // 输出:0
    }
}

代码解释

  1. 栈的使用:

    • 我们使用一个栈来记录遍历过程中商品的价格。栈的作用是帮助我们找到离当前商品最近的、符合条件的商品价格。
    • 对于每一个商品 p[i]p[i]p[i],我们将栈中所有大于 p[i]p[i]p[i] 的价格弹出,因为这些商品无法作为当前商品的优惠商品。
    • 一旦栈顶商品价格小于等于当前商品价格,我们就找到了最近满足条件的商品,将其价格加入到总优惠中。
  2. 主要操作:

    • 每遍历到一个商品 p[i]p[i]p[i],我们首先查看栈顶的商品。如果栈顶商品的价格大于当前商品价格,则弹出栈顶商品,直到找到合适的商品。
    • 如果栈中仍然有商品,则栈顶商品即为当前商品的优惠商品。
    • 当前商品价格无论如何都会被加入到栈中,以便它成为后续商品的可能优惠商品。
  3. 时间复杂度:

    • 栈操作(入栈和出栈)每个商品最多进行一次,因此整个过程的时间复杂度为 O(N)O(N)O(N),其中 NNN 是商品的数量。