英文原文
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 stackscapacity
.void push(int val)
Pushes the given integerval
into the leftmost stack with a size less thancapacity
.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 indexindex
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 topush
,pop
, andpopAtStack
.
中文题目
我们把无限数量 ∞ 的栈排成一行,按从左到右的次序从 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
- 最多会对
push
,pop
,和popAtStack
进行200000
次调用。
通过代码
高赞题解
由题目大意可知,题目的本质就是让我们快速找到 从右往左第一个不是空的栈
与 从左往右第一个非满栈
。
如果我们用树状数组 $tree$ 维护一个序列 $num$(支持Log复杂度单点修改/区间查询),第 $i$ 个位置维护的是第 $i$ 个栈中的元素个数,那么这两个问题我们都可以快速得到。(为了方便树状数组中的操作,将栈编号变为 1-Based)
从右往左第一个不是空的栈
我们可以很容易的使用一个变量跟踪餐盘栈中当前元素个数 $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$ 了。从左往右第一个非满栈
这个问题与之前的相似,我们的目标是找到一个最大的 $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)$。
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% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|