题意:
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6215
给出n个数的序列,每一轮需要删除当前这一轮比左边的数小或者比右边的数大的数字,然后再将剩下的部分合并,进行下一轮。最后序列变成非递减时结束,输出最后序列。
思路:
很显然每一轮就删掉一段递减的序列,比如,{1,2,3,7,6,5,8}删除的就是7,6,5,可以发现,每次删除一段后只会对这一段左右两边的数字有影响。
采用链表维护,每次删除后将左右两边的链表的合并起来,为了避免对已经正确排序的数字产生大量的重复扫描,所以在新一轮的待排序的head数组中只存放前一轮的删除段左边的第一个数字,比如上述例子新数组只存放3,然后每次从head数组开始扫描,找到递减段,删除后再次更新head数组即可。
详见代码。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;
int a[MAXN], nxt[MAXN], last[MAXN], head[MAXN];
int main() {
//freopen("in.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T--) {
int n, s = 0, t = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
nxt[i] = i + 1;
last[i] = i - 1;
head[t++] = i;
}
a[0] = 0; nxt[0] = 1; last[n + 1] = n;
int ans = n, flag = 1;
while (flag) {
int x = 0, s = 0;
flag = 0;
while (x < t) {
int now = head[x], cnt = 0;
while (nxt[now] <= n && a[now] > a[nxt[now]]) {
now = nxt[now]; ++cnt; flag = 1;
}
if (cnt) {
ans -= (cnt + 1);
nxt[last[head[x]]] = nxt[now];
last[nxt[now]] = last[head[x]];
head[s++] = last[head[x]];
}
while (head[x] <= now && x < t) ++x;
}
t = s;
}
printf("%d\n", ans);
int x = 0;
while (x <= n) {
if (x != 0) printf("%d ", a[x]);
x = nxt[x];
}
printf("\n");
}
return 0;
}