PAT A1057 Stack
Sample Input:
17
Pop
PeekMedian
Push 3
PeekMedian
Push 2
PeekMedian
Push 1
PeekMedian
Pop
Pop
Push 5
Push 4
PeekMedian
Pop
Pop
Pop
Pop
Sample Output:
Invalid
Invalid
3
2
2
1
2
4
4
5
3
Invalid
-
思路1:
push和pop很好实现,主要是peekmedian操作,如果对栈中所有元素排序会超时,所以用块状数组法(数组标记) -
code1:自己实现栈
#include <stdio.h>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn = 100010;
int stk[maxn];
int top = 0; //top == 当前栈中元素个数
int N;
int block[316] = {0}, table[maxn] = {0}; //存储块内元素的个数,i的个数
void findK(int k){
int sum = 0;
int nbb = 0; //记录所在块号
while(sum + block[nbb] < k){
sum += block[nbb++];
}
//第nbb块内的第 k - sum1 个元素
int num = 316*nbb;
while(sum + table[num] < k){
sum += table[num++];
}
cout << num <<endl;
}
void push(int v){
stk[top] = v;
top++;
int nb = v / 316;
block[nb]++;
table[v]++;
}
void pop(){
if(top > 0){
top--;
printf("%d\n", stk[top]);
int x = stk[top];
int x1 = x / 316;
block[x1]--;
table[x]--;
}
else printf("Invalid\n");
}
void peekmedian(){
//找第top/2 (分奇偶)的元素
if(top == 0) printf("Invalid\n");
else if(top%2==0){
//top为偶数,找第top/2大元素
findK(top/2);
}
else{
//top为奇数,找第top+1/2大元素
findK((top+1)/2);
}
}
void deal(string s, int v){
char c = s[1];
switch(c){
case 'u':
push(v);
break;
case 'o':
pop();
break;
case 'e':
peekmedian();
break;
}
}
int main(){
scanf("%d", &N);
string cmd;
int value;
for(int i = 0; i < N; ++i){
cin >> cmd;
if(cmd == "Push") scanf("%d", &value);
deal(cmd, value);
}
}
- code2: 使用stl栈
#include <stdio.h>
#include <string>
#include <algorithm>
#include <iostream>
#include <stack>
using namespace std;
const int maxn = 100010;
const int sqr = 316;
int N;
int block[sqr] = {0};
int table[maxn] = {0};
stack<int> st;
void findK(int k){
int sum = 0;
int id = 0;
while(sum + block[id] < k){
sum += block[id++];
}
int num = sqr * id;
while(sum + table[num] < k){
sum += table[num++];
}
printf("%d\n", num);
}
void push(int v){
st.push(v);
block[v / sqr]++;
table[v]++;
}
void pop(){
int x = st.top();
st.pop();
block[x / sqr]--;
table[x]--;
}
int main(){
scanf("%d", &N);
string cmd;
int value;
for(int i = 0; i < N; ++i){
cin >> cmd;
if(cmd == "Push"){
scanf("%d", &value);
push(value);
}
else if(cmd == "Pop"){
if(st.empty()) printf("Invalid\n");
else{
int x = st.top();
pop();
printf("%d\n", x);
}
}
else{
//PeekMedian
int n = st.size();
if(n == 0) printf("Invalid\n");
else if(n % 2 == 1) findK((n + 1) / 2);
else findK(n/2);
}
}
return 0;
}
- T2 code:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010, sqr = sqrt(100010.0);
int block[sqr], table[maxn];
stack<int> st;
bool GetMed(int n){
int sum = 0;
n = ceil(1.0 * n / 2);
if(n == 0) return false;
for(int i = 0; i < sqr; ++i){
if(sum + block[i] < n){
sum += block[i];
}else{
for(int j = i * sqr; j < (i + 1) * sqr; ++j){
if(sum + table[j] < n){
sum += table[j];
}else{
printf("%d\n", j);
return true;
}
}
}
}
return false;
}
int main(){
int n, tmp;
scanf("%d", &n);
for(int i = 0; i < n; ++i){
string com;
cin >> com;
if(com[1] == 'u'){ //push
scanf("%d", &tmp);
st.push(tmp);
table[tmp]++;
block[tmp / sqr]++;
}else if(com[1] == 'o'){ //pop
if(!st.empty() && table[st.top()] > 0){
tmp = st.top();
printf("%d\n", tmp);
st.pop();
table[tmp]--;
block[tmp / sqr]--;
}else{
printf("Invalid\n");
}
}else{ //med
if(GetMed(st.size()) == false){
printf("Invalid\n");
}
}
}
return 0;
}
-
思路 2:树状数组法
-
push
: 每入栈一个元素x,更新树状数组 下标[x] +1; -
pop
: 每出栈一个元素,更新树状数组 下标[x] -1; -
GetMed
:树状数组中GetSum(x) 可以得到位置x前(包括x),所有元素的和(因为每次+1,和即为个数);
GetSum(x) 为栈中小于等于x 的个数:GetSum(maxn) = st.size(); (因为栈中元素都比maxn小) ;
GetSum(x) 函数单调不减,故可用二分法找到mid,使得:GetSum(mid) >= st.size() / 2;
,说明栈中小于等于mid的元素个数至少有st.size() / 2
个,mid就是所求median(栈中第med大的元素) -
code:
#include <bits/stdc++.h>
#define lowbit(x) (x)&(-x)
using namespace std;
const int maxn = 100010;
int C[maxn];
stack<int> st;
int GetSum(int x){ // <=x 的个数
int sum = 0;
for(int i = x; i > 0; i -= lowbit(i)){
sum += C[i];
}
return sum;
}
void Update(int x, int v){
for(int i = x; i <= maxn; i += lowbit(i)){
C[i] += v;
}
}
int FindMedian(int l, int r, int x){
while(l < r){
int mid = (l + r) / 2;
int mid_sum = GetSum(mid);
if(mid_sum < x){
l = mid + 1;
}else{
r = mid;
}
}
return l;
}
int main(){
int n, tmp;
scanf("%d", &n);
for(int i = 0; i < n; ++i){
string com; cin >> com;
if(com[1] == 'u'){
scanf("%d", &tmp);
st.push(tmp);
Update(tmp, 1);
}else if(com[1] == 'o'){
if(!st.empty()){
tmp = st.top();
printf("%d\n", tmp);
st.pop();
Update(tmp, -1);
}else{
printf("Invalid\n");
}
}else{
int num = ceil(1.0 * st.size() / 2);
if(num == 0){
printf("Invalid\n");
}else{
printf("%d\n", FindMedian(1, maxn, num));
}
}
}
return 0;
}