Atcoder - abc273_e - Notebook(数据结构 + 思维)
abc273_e - Notebook(⇔源地址)
tag
⇔数据结构、⇔思维。
题意
给出一本 \(10^9\) 页的笔记本和一个句子,初始时笔记本每一页都是空白的,句子也是空的,现在进行如下操作:
- 在句子的末尾加上数字 \(x\) ;
- 去掉句子末尾的数字(如果句子为空则跳过);
- 将句子记录到笔记本的第 \(y\) 页;
- 将笔记本的第 \(z\) 页的内容输出到句子中;
对于每一个操作,输出当前句子末尾的那个数字(如果句子为空则输出 \(-1\) )。
思路
刚开始以为是暴力模拟题,直接写了一个栈来代表句子,写了一个 \(\tt map\) 来代表笔记本,每次都将当前的栈完整的储存到 \(\tt map\) 里面去。结果因为栈开的太大莫名RE,改小了之后又不出所料的超时了,可惜这个时候已经没时间改思路了,遗憾下班。
正解
由于需要记录、读取状态,所以我们需要将全部的操作储存下来,即删除并不代表将其真的删除,那么我们如何实现删除操作呢,联想到指针——可以建立一个类似于链表的结构,使得每一个元素都可以通过 \(pre\) 指针找到其上一级。所以上述的操作转变为:
-
首先定义节点 \(\{R_{编号}:值\}\) ,其包含两个值:节点编号、节点值。由于节点不删除,所以编号各不相同。
-
增加:在链表末尾新建一个节点 \(\{R_{len}:x\}\) :其编号为当前链表长度 \(len\) ,其值为 \(x\) 。其为新的末尾节点。随后创建一个 \(pre\) 指针指向其前一级节点。
-
删除:通过 \(pre\) 指针找到当前末尾节点 \(\{R_{len}:x\}\) 的上一级,其为新的末尾节点;
-
添加:创建一个 \(\tt map\) 数组,直接记录当前链表末尾节点的节点编号;
-
读取:直接从 \(\tt map\) 数组中提取节点编号,随后,通过编号我们可以唯一的确定那个节点,并且能够获取其值和 \(pre\) 指针。
这样,我们可以通过一个链表来完整的记录所有的增加和删除;通过一个 \(\tt map\) 数组来完整的储存所有的添加和读取,并且可以通过链表来继续进行增加和删除工作。
后日谈
赛后补题重现赛时的思路,很容易的可以发现:将栈储存到 \(\tt map\) 中这一操作,由于每次需要遍历栈中全部元素,所以极其耗费时间,而且也极其的耗费内存,所以显然是错误的。
AC代码
点击查看代码
int a[N] = {-1}, pre[N], alen, now;
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n; cin >> n;
map<int, int> dic;
for (int i = 1; i <= n; ++ i) {
string op; cin >> op;
char x = op[0];
if (x == 'A') {
int x; cin >> x;
a[++ alen] = x; //当前新增的节点在链表末尾
pre[alen] = now; //将新增的节点与原节点建立关系
now = alen;
}
else if (x == 'D' && alen > 0) {
now = pre[now]; //跳回当前节点的上一个
}
else if (x == 'S') {
int x; cin >> x;
dic[x] = now;
}
else if (x == 'L') {
int x; cin >> x;
now = dic[x];
}
cout << a[now] << " ";
}
return 0;
}
错误次数:2
文 / WIDA
2022.10.24 成文
首发于WIDA个人博客,仅供学习讨论