这是我们考试题,我做了 $75$ 分,最后才发现是元素入队的时候没有赋值……
Problem
有一个长度为 $n$ 的整数数组,对数组执行若干次操作。每一次找到连续相等整数的最长段(如果有多个段长度相同,选择最靠左边的段)并删除它。要求计算经过多少次操作后数组为空。
Solution
因为前两天才调完小熊的果篮,记忆犹新,所以立刻想到了队列+链表的做法。然后再读题,发现需要找长度最长的区间,不能用普通队列,要用优先队列。
我们先把所有的区间都找出来,存储每一个区间的左端点,长度(这样就可以算出右端点了)和这个区间数字的值。然后把这些区间全部按照顺序弄到一个双向链表里面,并把需要的值加入优先队列。
详细的讲解在代码注释里。
1 | // 定义结构体,需要存区间左端点,区间长度和它在链表里的下标 |
接着就是核心代码了。
我们用一个 bool
数组标记一个区间是否被取过,然后从队列里面不停取元素。
- 若此区间已经被标记过了就直接跳过。
- 若此区间没有被取过,就标记一下这个区间,此时又取了一个区间,答案需要 $+1$,然后检查一下它的左右两个区间是否需要被合并;如果需要,就把两个区间合并到它左边那个区间,并标记它右边那个区间。
这句话信息量有点大,是什么意思呢?
说明我们拿到一个没被取过的区间时,需要做这几件事:
- 把这个区间标记为“已经被取过”。
- 因为又取了一个区间,所以答案要 $+1$。
- 把这个区间从链表里删掉。
- 检查这个被删除的区间的左边的区间(命名为 $l$)和它右边的区间(命名为 $r$)是否需要被合并,也就是说这两个区间的值是不是一样的,如果是一样的,那这个区间被取了之后,$l$ 和 $r$ 就变成了一个区间,所以需要被合并。
- 合并两个区间的时候,可以把 $l$ 的长度改为两个区间的长度相加,然后把 $r$ 删掉。需要注意 $r$ 也要被标记。
- 把更新的 $l$ 加入队列。
这个时候就会有小朋友问了:此时队列里还有原来的 $l$,是不是需要删掉?
答案是不需要。因为优先队列的排序规则是按照长度从大到小排的,所以更新后的 $l$ 一定会比原来的 $l$ 先取出,取出之后我们就标记了 $l$,也就不会重复取到了。
给一下代码。
1 | inline void remove(int x) { |
大概就是这样了吧。因为核心代码已经给出,所以不再给完整代码了。
另外就是祝贺一下 ljt 考试的时候终于没有写挂快读快写了。
Invitation
Liynw
30359624
created:25/12/2007
This is my QQ!
这个人是个不可爱的 BUG 制造机,不过 TA 还是很喜欢瞎搞。
祝您拥有愉快的一天~
评论