加载中...
1172-餐盘栈(Dinner Plate Stacks)
发表于:2021-12-03 | 分类: 困难
字数统计: 1.9k | 阅读时长: 9分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/dinner-plate-stacks

英文原文

You have an infinite number of stacks arranged in a row and numbered (left to right) from 0, each of the stacks has the same maximum capacity.

Implement the DinnerPlates class:

  • DinnerPlates(int capacity) Initializes the object with the maximum capacity of the stacks capacity.
  • void push(int val) Pushes the given integer val into the leftmost stack with a size less than capacity.
  • int pop() Returns the value at the top of the rightmost non-empty stack and removes it from that stack, and returns -1 if all the stacks are empty.
  • int popAtStack(int index) Returns the value at the top of the stack with the given index index and removes it from that stack or returns -1 if the stack with that given index is empty.

 

Example 1:

Input
["DinnerPlates", "push", "push", "push", "push", "push", "popAtStack", "push", "push", "popAtStack", "popAtStack", "pop", "pop", "pop", "pop", "pop"]
[[2], [1], [2], [3], [4], [5], [0], [20], [21], [0], [2], [], [], [], [], []]
Output
[null, null, null, null, null, null, 2, null, null, 20, 21, 5, 4, 3, 1, -1]

Explanation: 
DinnerPlates D = DinnerPlates(2);  // Initialize with capacity = 2
D.push(1);
D.push(2);
D.push(3);
D.push(4);
D.push(5);         // The stacks are now:  2  4
                                           1  3  5
                                           ﹈ ﹈ ﹈
D.popAtStack(0);   // Returns 2.  The stacks are now:     4
                                                       1  3  5
                                                       ﹈ ﹈ ﹈
D.push(20);        // The stacks are now: 20  4
                                           1  3  5
                                           ﹈ ﹈ ﹈
D.push(21);        // The stacks are now: 20  4 21
                                           1  3  5
                                           ﹈ ﹈ ﹈
D.popAtStack(0);   // Returns 20.  The stacks are now:     4 21
                                                        1  3  5
                                                        ﹈ ﹈ ﹈
D.popAtStack(2);   // Returns 21.  The stacks are now:     4
                                                        1  3  5
                                                        ﹈ ﹈ ﹈ 
D.pop()            // Returns 5.  The stacks are now:      4
                                                        1  3 
                                                        ﹈ ﹈  
D.pop()            // Returns 4.  The stacks are now:   1  3 
                                                        ﹈ ﹈   
D.pop()            // Returns 3.  The stacks are now:   1 
                                                        ﹈   
D.pop()            // Returns 1.  There are no stacks.
D.pop()            // Returns -1.  There are still no stacks.

 

Constraints:

  • 1 <= capacity <= 2 * 104
  • 1 <= val <= 2 * 104
  • 0 <= index <= 105
  • At most 2 * 105 calls will be made to push, pop, and popAtStack.

中文题目

我们把无限数量 ∞ 的栈排成一行,按从左到右的次序从 0 开始编号。每个栈的的最大容量 capacity 都相同。

实现一个叫「餐盘」的类 DinnerPlates

  • DinnerPlates(int capacity) - 给出栈的最大容量 capacity
  • void push(int val) - 将给出的正整数 val 推入 从左往右第一个 没有满的栈。
  • int pop() - 返回 从右往左第一个 非空栈顶部的值,并将其从栈中删除;如果所有的栈都是空的,请返回 -1
  • int popAtStack(int index) - 返回编号 index 的栈顶部的值,并将其从栈中删除;如果编号 index 的栈是空的,请返回 -1

 

示例:

输入: 
["DinnerPlates","push","push","push","push","push","popAtStack","push","push","popAtStack","popAtStack","pop","pop","pop","pop","pop"]
[[2],[1],[2],[3],[4],[5],[0],[20],[21],[0],[2],[],[],[],[],[]]
输出:
[null,null,null,null,null,null,2,null,null,20,21,5,4,3,1,-1]

解释:
DinnerPlates D = DinnerPlates(2);  // 初始化,栈最大容量 capacity = 2
D.push(1);
D.push(2);
D.push(3);
D.push(4);
D.push(5);         // 栈的现状为:    2  4
                                    1  3  5
                                    ﹈ ﹈ ﹈
D.popAtStack(0);   // 返回 2。栈的现状为:      4
                                          1  3  5
                                          ﹈ ﹈ ﹈
D.push(20);        // 栈的现状为:  20  4
                                   1  3  5
                                   ﹈ ﹈ ﹈
D.push(21);        // 栈的现状为:  20  4 21
                                   1  3  5
                                   ﹈ ﹈ ﹈
D.popAtStack(0);   // 返回 20。栈的现状为:       4 21
                                            1  3  5
                                            ﹈ ﹈ ﹈
D.popAtStack(2);   // 返回 21。栈的现状为:       4
                                            1  3  5
                                            ﹈ ﹈ ﹈ 
D.pop()            // 返回 5。栈的现状为:        4
                                            1  3 
                                            ﹈ ﹈  
D.pop()            // 返回 4。栈的现状为:    1  3 
                                           ﹈ ﹈   
D.pop()            // 返回 3。栈的现状为:    1 
                                           ﹈   
D.pop()            // 返回 1。现在没有栈。
D.pop()            // 返回 -1。仍然没有栈。

 

提示:

  • 1 <= capacity <= 20000
  • 1 <= val <= 20000
  • 0 <= index <= 100000
  • 最多会对 pushpop,和 popAtStack 进行 200000 次调用。

通过代码

高赞题解

由题目大意可知,题目的本质就是让我们快速找到 从右往左第一个不是空的栈从左往右第一个非满栈

如果我们用树状数组 $tree$ 维护一个序列 $num$(支持Log复杂度单点修改/区间查询),第 $i$ 个位置维护的是第 $i$ 个栈中的元素个数,那么这两个问题我们都可以快速得到。(为了方便树状数组中的操作,将栈编号变为 1-Based)

  1. 从右往左第一个不是空的栈
    我们可以很容易的使用一个变量跟踪餐盘栈中当前元素个数 $siz$,那么原问题等于我们要找到一个最小的 k,满足 $\sum_{i=1}^k num[i] = siz$。那么这个 $k$ 就是从右往左第一个非空的栈。
    找最小的 $k$ 这个问题可以使用倍增解决,因为如果有一个 $k^1>k$ ,那么一定有 $\sum_{i=1}^{k^1} num[i] = siz$。所以,我们可以从高到低枚举 $k-1$ 的二进制位(因为 $k$ 是最小的,所以 $k-1$ 一定不满足上述条件),看看这一位上 $1$ 之后,是否能让 $k-1$ 满足条件,若满足就不上否则上。求出来 $k-1$ 之后,再加一就是 $k$ 了。

  2. 从左往右第一个非满栈
    这个问题与之前的相似,我们的目标是找到一个最大的 $k$,使其满足 $k*capacity = \sum_{i=1}^k num[i]$。那么,$k+1$ 就是从左往右第一个没有满的栈。类似的也可以使用倍增解决。

说到这里,这个问题已经被解决了,我们的总时间复杂度就是 $O(n log^2 n)$ 的。

但是还有进一步优化的空间,我们知道树状数组 $tree[k]=\sum_{i=k-lowbit(k)+1}^k num[i]$,也就是说如果我们知道了 $\sum_{i=1}^k num[i] = A$ 且 $(1<<j)<lowbit(k)$,那么 $\sum_{i=1}^{k+(1<<j)} = A + tree[k+(1<<j)]$。
倍增的时候枚举二进制位的时候,恰巧我们也是从大到小枚举的,满足 j 与 k 的限制。
如此一来,在倍增的时候,我们就可以将一次树状数组上 $Log$ 复杂的的查询,替换成一次简单的加法。

至此,问题的时间复杂度就降低为 $O(n log n)$。

餐盘栈性能.PNG

namespace BitTree{
    const int MAXN=200000;
    int sum[MAXN+5];
    
    inline void init(){ memset(sum, 0, sizeof(sum)); }
    
    inline int lowbit(int x){ return x&-x; }

    inline void add(int x, int v){ 
        for (int i=x; i<=MAXN; i+=lowbit(i)) sum[i]+=v; 
    }

    inline int get(int x){
        int ret=0;
        for (int i=x; i; i-=lowbit(i)) ret+=sum[i];
        return ret;
    }
}

const int MAXN=200000;
static stack<int> stk[MAXN+5];

class DinnerPlates {
public:
    int cap, siz;
    
    DinnerPlates(int capacity) {
        BitTree::init();
        cap=capacity, siz=0;
        for (int i=0; i<=MAXN; i++) while(!stk[i].empty()) stk[i].pop();
    }
    
    int getFree(){
        int rate=0, ans=0;
        for (int i=14; i>=0; i--){
            if (ans+(1<<i) > MAXN) continue;
            if (rate+BitTree::sum[ans+(1<<i)] == 1LL*cap*(ans+(1<<i))) rate+=BitTree::sum[ans+=(1<<i)];
        }
        return ans+1;
    }
    
    int getRight(int c){
        int rate=0, ans=0;
        for (int i=14; i>=0; i--){
            if (ans+(1<<i) > MAXN) continue;
            if (rate+BitTree::sum[ans+(1<<i)] < c) rate+=BitTree::sum[ans+=(1<<i)];
        }
        return ans+1;
    }
    
    
    void push(int val) {
        int p=getFree();
        
        stk[p].push(val);
        ++siz;
        BitTree::add(p, 1);
    }
    
    int pop() {
        if (siz==0) return -1;
        
        int p=getRight(siz);
        int r=stk[p].top(); 
        
        stk[p].pop();
        --siz;
        BitTree::add(p, -1);
        
        return r;
    }
    
    int popAtStack(int index) {
        if (stk[index+1].empty()) return -1;
        
        int r=stk[index+1].top(); 
        
        stk[index+1].pop();
        --siz;
        BitTree::add(index+1, -1);
        
        return r;
    }
};

统计信息

通过次数 提交次数 AC比率
2565 9685 26.5%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言
上一篇:
1171-从链表中删去总和值为零的连续节点(Remove Zero Sum Consecutive Nodes from Linked List)
下一篇:
1170-比较字符串最小字母出现频次(Compare Strings by Frequency of the Smallest Character)
本文目录
本文目录