2017.10.17 T2 1990
样例数据
输入
4
5
1 2 3 4 5
5
5 4 3 2 1
5
1 2 3 2 1
5
2 4 1 3 5
输出
5
1 2 3 4 5
02
1 2
3
2 3 5
分析:T3的博客也说过了,哎,这道题很痛。由于每次扫一遍删除不合法序列是O(
N2
)的,我们要想办法优化它,想起才考的跑步一题,用双向链表将O(
N2
)降为O(N),十分巧妙,是不是这道题也可以用呢?答案当然是肯定的。
我们先扫一次原序列,将不合法的点打上标记(此时已经保证没打标记的部分是一段段单调递增的序列),将一段不合法的点的左端点左边的数和右端点右边的数,也就是删除不合法序列后连接在了一起的点,加入队列(因为删掉不合法的点之后这两个会并到一起,而由于这两个新加点都存在于各自单调递增的序列中,所以它们的左边(右边)的点都还是合法的,也就不存在会和它们一起被删的情况),再对队列进行扫描,同理加入(当然要判断一下加入的点在不在序列中、是不是已经被删掉了,如果是那就不加),这样相当于就不用把整个序列都扫一遍而是找到了删点的关键,大大节省了时间。每次O(1)删除不和谐数,然后再依次检查合并或者删除子串,细节较多,详见代码。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
const int N=1e5+10;
const int INF=0x3f3f3f3f;
int T,a[N],b[N],n,cnt,tot,p,ans;
bool exist[N],del[N],bj;
struct node{
int pos;
int val;
}que[N];
int main()
{
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
T=getint();
while(T--)
{
memset(del,false,sizeof(del));//清零操作
memset(exist,false,sizeof(exist));
bj=false;cnt=0;
n=getint();
for(int i=1;i<=n;++i)
a[i]=getint();
a[0]=-INF,a[n+1]=INF;//要让a[1]、a[n]也能比较,就加两个边界
for(int i=1;i<=n;++i)
if(a[i]>a[i+1]||a[i]<a[i-1])//第一遍扫,找不合法(所有的单调递减序列都被剔除)
{
del[i]=true;
bj=true;
}
for(int i=1;i<=n;++i)
{
if(del[i]==true)//添加删除后挨在一起的点到队列中
{
if(i>1&&del[i-1]==false&&exist[i-1]==false)
que[++cnt].pos=i-1,que[cnt].val=a[i-1],exist[i-1]=true;
if(i<n&&del[i+1]==false&&exist[i+1]==false)
que[++cnt].pos=i+1,que[cnt].val=a[i+1],exist[i+1]=true;
}
}
while(bj==true)//只要还有不合法的点就要继续删
{
que[0].val=-INF,que[cnt+1].val=INF;//清零操作
bj=false,tot=0;
for(int i=1;i<=cnt;++i)
{
if(que[i].val>que[i+1].val||que[i].val<que[i-1].val)//不合法
{
del[que[i].pos]=true;//删除标记
exist[que[i].pos]=false;//出队列标记
bj=true;
}
}
for(int i=1;i<=cnt;++i)
{
p=que[i].pos;
if(del[p])//同上加入点
{
if(p>1&&del[p-1]==false&&exist[p-1]==false)
que[++tot].pos=p-1,que[tot].val=a[p-1],exist[p-1]=true;
if(p<n&&del[p+1]==false&&exist[p+1]==false)
que[++tot].pos=p+1,que[tot].val=a[p+1],exist[p+1]=true;
}
else//如果还合法就留着
que[++tot]=que[i];
}
cnt=tot;//队列长度更新
}
ans=0;
for(int i=1;i<=n;++i)
if(del[i]==false) ans++,b[ans]=a[i];
cout<<ans<<'\n';
for(int i=1;i<=ans;++i)
cout<<b[i]<<" ";
cout<<'\n';
}
return 0;
}
本题结。