P7912 [CSP-J 2021] 小熊的果篮
题目描述
小熊的水果店里摆放着一排
题解
首先对输入序列建双向链表,维护每一个“假块”头建双向链表,共维护两个链表。
这里的“假块”指每个“假块”中水果种类必定相同,但相邻“假块”中水果种类可能相同。
我们可以使用双向链表的删除元素来模拟吃一个水果。
不断循环遍历“假块”头链表,遍历过程中记录上一个被吃水果种类。遍历到某个块头时,若其指向的水果与上一个被吃水果种类相同,直接将这个块头删除,相当于合并块;若不同,吃掉这个水果,更新上一个被吃水果种类,将这个块头指向的水果变成被吃水果的下一个水果。
关于一个假块被吃完的处理方法,此时这个假块的块头一定会指向下一个块头。若这个块头的种类与被吃水果的种类不同,删掉这个块,因为遍历下一个块时将会吃掉这个水果;若相同,不动,因为接下来的过程将会把下一个块的块头删除。这样保证遍历时不会出现长度小于一的假块。若假块没有被吃完,其指向的下一个水果一定与吃掉的种类相同,同样不做任何处理。
每个水果被遍历一次,每个块被删除一次,时间复杂度
代码如下:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int MAXN = 2e5+10;
struct Node{
int prev;
int val;
int next;
};
int n;
int shuiguo[MAXN];
Node headList[MAXN];
Node shuiguoList[MAXN];
int cc;
void EatOne(int pos) {
int prev = shuiguoList[pos].prev;
int next = shuiguoList[pos].next;
shuiguoList[prev].next = next;
shuiguoList[next].prev = prev;
printf("%d ", pos);
}
void Del(int pos) {
int prev = headList[pos].prev;
int next = headList[pos].next;
headList[prev].next = next;
headList[next].prev = prev;
}
void Chi() {
int solo = headList[0].next;
int nowcolor = shuiguo[headList[solo].val];
while (solo!=cc+1) {
if (shuiguo[headList[solo].val]!=nowcolor) {
Del(solo);
solo = headList[solo].next;
continue;
}
EatOne(headList[solo].val);
headList[solo].val = shuiguoList[headList[solo].val].next;
if (shuiguo[headList[solo].val]!=nowcolor) {
Del(solo);
}
solo = headList[solo].next;
nowcolor^=1;
}
putchar('\n');
}
int main() {
scanf("%d", &n);
shuiguoList[0].next = 1;
for (int i = 1; i <= n; i++) {
scanf("%d", &shuiguo[i]);
shuiguoList[i].prev = i-1;
shuiguoList[i].next = i+1;
}
shuiguo[0] = 2;
shuiguo[n+1] = 2;
headList[0].next = 1;
for (int i = 1; i <= n; i++) {
if (shuiguo[i]!=shuiguo[i-1]) {
cc++;
headList[cc].prev = cc-1;
headList[cc].next = cc+1;
headList[cc].val = i;
}
}
while (shuiguoList[0].next!=n+1) {
Chi();
}
return 0;
}