B3614 【模板】栈(传送门)
【模板】栈
题目描述
请你实现一个栈(stack),支持如下操作:
push(x)
:向栈中加入一个数 x x x。pop()
:将栈顶弹出。如果此时栈为空则不进行弹出操作,输出Empty
。query()
:输出栈顶元素,如果此时栈为空则输出Anguei!
。size()
:输出此时栈内元素个数。
输入格式
本题单测试点内有多组数据。
输入第一行是一个整数
T
T
T,表示数据组数。对于每组数据,格式如下:
每组数据第一行是一个整数,表示操作的次数
n
n
n。
接下来
n
n
n 行,每行首先由一个字符串,为 push
,pop
,query
和 size
之一。若为 push
,则其后有一个整数
x
x
x,表示要被加入的数,
x
x
x 和字符串之间用空格隔开;若不是 push
,则本行没有其它内容。
输出格式
对于每组数据,按照「题目描述」中的要求依次输出。每次输出占一行。
样例 #1
样例输入 #1
2
5
push 2
query
size
pop
query
3
pop
query
size
样例输出 #1
2
1
Anguei!
Empty
Anguei!
0
提示
样例 1 解释
对于第二组数据,始终为空,所以 pop
和 query
均需要输出对应字符串。栈的 size 为 0。
数据规模与约定
对于全部的测试点,保证 1 ≤ T , n ≤ 1 0 6 1 \leq T, n\leq 10^6 1≤T,n≤106,且单个测试点内的 n n n 之和不超过 1 0 6 10^6 106,即 ∑ n ≤ 1 0 6 \sum n \leq 10^6 ∑n≤106。保证 0 ≤ x < 2 64 0 \leq x \lt 2^{64} 0≤x<264。
提示
- 请注意大量数据读入对程序效率造成的影响。
- 因为一开始数据造错了,请注意输出的
Empty
不含叹号,Anguei!
含有叹号。
刚拿到这个题时,我先不假思索地尝试了最简单的静态数组写法,但是……
看一下数据范围:
n
n
n能到
1
0
6
10^6
106,x能到
2
64
2^{64}
264,说明
x
x
x要用无符号长整型
u
n
s
i
g
n
e
d
l
o
n
g
l
o
n
g
unsigned\ long\ long
unsigned long long,用静态数组显然会炸掉(RE)。
0分代码 (建议别看) :
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define ull unsigned long long
using namespace std;
const int MAXN = 10007;
typedef struct {
ull data[MAXN];
int top;
}SqStack;
inline bool StackEmpty(SqStack& S) {
return S.top == -1 ? true : false;
}
inline void Push(SqStack& S, ull x) {
S.data[++S.top] = x;
return;
}
inline void Pop(SqStack& S) {
if (StackEmpty(S))printf("Empty\n");
else S.top--;
return;
}
inline void Query(SqStack& S) {
if (StackEmpty(S))printf("Anguei!\n");
else printf("%llu\n", S.data[S.top]);
return;
}
inline void Opt() {
int n;
scanf("%d", &n);
ull x;
char ch;
SqStack S;
S.top = -1;
while (n--) {
ch = getchar();
if (ch == 'p') {
ch = getchar();
if (ch == 'u') {
ch = getchar(),ch = getchar();
scanf(" %llu", &x);
Push(S, x);
}
else if (ch == 'o') {
ch = getchar(), ch = getchar();
Pop(S);
}
}
else if (ch == 'q') {
ch = getchar(), ch = getchar(), ch = getchar(), ch = getchar(), ch = getchar();
Query(S);
}
else if (ch == 's') {
ch = getchar(), ch = getchar(), ch = getchar(), ch = getchar();
printf("%d\n", S.top + 1);
}
}
return;
}
int main() {
int T;
scanf("%d", &T);
for (int i = 1; i <= T; ++i)
Opt();
return 0;
}
实际上,如果MAXN
开到
1
e
6
+
7
1e6+7
1e6+7的话,本地编译都过不了,内存直接爆炸(骗分失败
而且我这里读指令也用了个很“鸡贼”的写法,用
g
e
t
c
h
a
r
(
)
getchar()
getchar()一个一个读来判断,看起来好像有那么回事,其实跑到最后甚至没法结束输入……
鉴定为太久没打OI导致的,细节都做得很差,痛定思痛,于是我准备用正规的链栈写法来做。
出乎意料的是,一遍就直接AC了,嗯,不错。
具体实现我会在代码的注释里给出讲解,需要的前置知识有:单链表,栈的基本概念。
满分代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>//输入输出流必备头文件
#define ull unsigned long long//用ull替换unsigned long long让代码更简洁
using namespace std;
typedef struct SNode{
int len;//记录表长(栈内元素个数)
ull data;//记录数据元素
struct SNode* next;
}SNode,* SqStack;//单链表形式定义栈
inline void InitStack(SqStack& S) {//初始化栈
S = (SNode*)malloc(sizeof(SNode));//分配头结点
S->next = NULL;
S->len = 0;//初始表长为0
return;
}
inline bool StackEmpty(SqStack& S) {//判空
return S->len == 0 ? true : false;//表长为0则为空栈
}
inline void Push(SqStack& S, ull x) {//入栈
SNode* p = (SNode*)malloc(sizeof(SNode));//分配新结点
if (p == NULL)return;//分配失败
p->data = x;//记录新数据
p->next = S->next;//将新节点p的后继指向栈顶元素的后继
S->next = p;//栈顶元素的后继再指向p,完成插入
S->len++;//表长+1
return;
}
inline void Pop(SqStack& S) {//出栈
if (StackEmpty(S))printf("Empty\n");//若表长为0则为空栈,按题意输出“Empty”
else {
SNode* p = S->next;//否则,用临时结点p指向栈顶元素
S->next = p->next;//栈顶元素的后继指向其后继的后继
free(p);//释放栈顶元素,完成出栈
S->len--;//表长-1
}
return;
}
inline void Query(SqStack& S) {//询问栈顶元素
if (StackEmpty(S))printf("Anguei!\n");//若表长为0则为空栈,按题意输出“Anguei!”
else printf("%llu\n", S->next->data);//否则输出栈顶元素数据
return;
}
int main() {
int T,n;
std::cin >> T;//std输入流
string str;//记录操作字符串
ull x;
for (int i = 1; i <= T; ++i) {//T组数据
std::cin >> n;
SqStack S;//声明一个栈
InitStack(S);
for (int j = 1; j <= n; ++j) {//n次操作
std::cin >> str;//std输入流,遇空格即停止,这一点比scanf好用
if (str == "push") {//若为push,则进行入栈操作
std::cin >> x;
Push(S, x);
}
else if (str == "pop") {//若为pop,则进行出栈操作
Pop(S);
}
else if (str == "query") {//若为query,则进行询问栈顶元素操作
Query(S);
}
else if (str == "size") {//若为size,则输出表长
printf("%d\n", S->len);
}
}
}
return 0;
}
所以说,想当然或者投机取巧都是不行的,还是要老老实实、脚踏实地打代码。
感觉写个普及-
的题都汗流浃背了,自己代码功力退化的还是很严重啊。
继续加油吧。