先扫一遍数组,求出所有的“块”,用结构体存储,再用链表串起来。
再扫描链表,每次取出一篮水果。每扫完一遍,检查并清除链表中的空块。不断扫描直到链表为空。
对于“两个相邻且水果类型相同的块变为一个块”,如果合并两个结点,会导致水果的序号难以处理,不建议使用。可以判断每个结点的水果类型是否与其前驱结点的水果类型相同,若相同,则跳过该结点。
需要注意的是,当某个块变成空块时,即使它的前驱结点和后继结点的水果类型相同,仍然要从它的后继节点中取出最左边的水果(详见样例一)。故可以将取水果和删空块分为两个循环执行。
时间复杂度为 O(n) 。
code:
#include <bits/stdc++.h>
#define head link[1]
#define tail link[2]
using namespace std;
struct Node{
int l, len, flag, next, prev;//l为该块中最左边的水果编号,len为水果数
}link[200005];
int tot = 2;
int n, a[200005];
int main()
{
head = (Node){0, -1, -1, 2,-1}, tail = (Node){0, -1, -1, -1, 1};//链表头尾
scanf("%d", &n);
memset(a, 0x3f, sizeof a);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
for (int i = 1; i <= n;)
{
int cnt = 0, k = a[i], tmp = i;
while (a[i] == k)
{
cnt++;
i++;
}//求块
tot++;
link[tot].l = tmp;
link[tot].len = cnt;
link[tot].flag = k;
link[tot].next = 2;
link[tot].prev = tail.prev;
link[tail.prev].next = tot;
tail.prev = tot;//建立结点并插入链表尾部
}
while (head.next != 2)//链表不为空
{
for (int i = head.next; i != 2; i = link[i].next)
{
if (link[link[i].prev].flag == link[i].flag) continue;
printf("%d ", link[i].l);
link[i].l++;
link[i].len--;
}//取水果并输出
for (int i = head.next; i != 2; i = link[i].next)
{
if (link[i].len == 0)
{
link[link[i].prev].next = link[i].next;
link[link[i].next].prev = link[i].prev;
}
}//删空块
putchar('\n');
if (link[tail.prev].len == 0)
{
tail.prev = link[tail.prev].prev;
link[tail.prev].next = 2;
}
}
return 0;
}