ABC-273解题报告
D. LRUD Instructions
题意:一个左上角为 、右下角为 的矩阵,矩阵中有 个障碍。你初始在 ,给你一个操作序列,每个操作为向上/下/左/右走若干格,如果遇到障碍/走到边界则停止。每次操作后输出当前位置。
用数据结构存下每行中的障碍和每列中的障碍,每次从中找到最近的障碍,如果被挡住则走到障碍的上一格,否则正常走。
best code:
By tourist
1 |
|
这份代码有两个好的地方:
- 使用
map
套set
来维护障碍,避免了复杂的离散化处理。 - 将越界情况统一处理,每次操作最后使用四行 来处理越界。
By WA_TLE
1 |
|
这份代码巧妙地利用了 pair
自带比较函数的特点,使用正反两个 pair
数组维护障碍,同样避免了离散化且实现巧妙。
By Factorio
1 |
|
这份代码的巧妙之处在于,每次询问先把朝向的边界加入 set
,使三种情况得到统一,更好地避免了分类讨论。
worse code:
By cxm1024
1 |
|
这份代码直接使用离散化后的 vector
数组来存储障碍,且用了过多分类讨论,实现繁琐。
改进版本:
By cxm1024
1 |
|
wrong code:
By MMNMM(TLE)
1 |
|
这份代码 TLE 的原因在于第 35、43、51、59 行为了简化代码将 set 复制了一份,而 set 的直接复制是 的,所以导致超时。如果要复制的话,可以选择按引用赋值,即将 const auto walls{vertical[now.first]};
改为 const auto &walls=vertical[now.first];
便不会超时。该语句相当于指向了原 set
而不会重新复制一份。
选自《Effective C++》:
The meaning of passing an object by value is defined by the copy constructor of that object’s class. This can make pass-by-value an extremely expensive operation.
E. Notebook
题意:你需要维护一个序列,支持:
push_back
、pop_back
、记录一个新版本、回退版本。
首先看到这题,第一反应想到用可持久化的数据结构来维护,但对于这道题而言显然过于麻烦了。于是想到可以通过依赖树,将可持久化问题转为非可持久化来做,建图跑 DFS 即可。
By cxm1024
1 |
|
然而这个做法仍然不够简洁。注意到由于只需要支持 push_back
和 pop_back
这两个较为基础的操作,我们可以使用链表维护。这里使用反向链表,并维护当前的末尾指针,则每次生成新版本只需要记录当前的末尾即可拉出来一整条链。push_back
只需要增加一个元素作为新末尾并指向原末尾;pop_back
则不需要真的删除元素,单纯把当前末尾指针回退一格即可。显然时空复杂度是可以接受的。
By tourist
1 |
|
维护 notebook 也可以不用 map 而使用哈希:
时间复杂度上可以优化掉一个 ,思路,没有本质区别。
这道题本质上是维护一个可持久化栈,经典的做法就是用链表维护。我们也可以将其封装起来,作为一个专门的数据结构:
By sapphire15
1 |
|