Almost Sorted Array
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 3328 Accepted Submission(s): 835
We say an array is sorted if its elements are in non-decreasing order or non-increasing order. We say an array is almost sorted if we can remove exactly one element from it, and the remaining array is sorted. Now you are given an array , is it almost sorted?
There are at most 20 test cases with .
3 3 2 1 7 3 3 2 1 5 3 1 4 1 5
YES YES NO
题意:给定一个数组,是否删去某个元素后该数组单调增或单调减,可以则输出“YES”不能则输出“NO";
寻找数组序列的LIS,顺逆序都找一次,然后看是否有满足长度>=n-1,有则“YES”否则“NO”;
LIS:Longest Increasing Sequence 最长上升公共序列,不一定连续,不严格递增;
寻找LIS,我们可以通过双重循环来寻找,但是O(n^2)的话会TLE,因此考虑用LIS的二分优化将时间将为O(nlogn);
原理:
用a[i]储存原序列,d[i]储存LIS的长度(这个数组的长度是LIS的长度,但是不是LIS),遍历a[i],如果a[i]小于d数组当前的数,就把它放进d数组覆盖当前的数;如果比它大(或等于)就把它插到d数组当前数之前比它大的那一位,再覆盖;
举个例子:
a:3 1 4 3 5 2
d:3
d:1
d:1 4
d:1 3
d:1 3 5
d:1 2 5
(用队列实现起来也很方便)
如果要寻找数字的话,优化起来就应该想到二分;
当a[i]小于d当前数字时,在d数组当前位置之前(不包括该位置)中二分寻找a[i]最后一个大于等于a[i]的位置,将该位置的值赋给a[i](用upper_bound函数来实现,三个参数分别为寻找的首地址,末地址,键值);
当a[i]大于等于d当前数字时,将a[i]赋值给下一位d;
最后正反两次LIS,寻找符合条件的长度;
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=100005;
int d[N],a[N];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int len1=1,len2=1;
d[len1]=a[1];
for(int i=2;i<=n;i++){
if(a[i]>=d[len1])//正序LIS
len1++,d[len1]=a[i];
else {
int pos=upper_bound(d+1,d+len1,a[i])-d;
d[pos]=a[i];
}
}
d[len2]=a[n];
for(int i=n-1;i>=1;i--){
if(a[i]>=d[len2])//逆序LIS
len2++,d[len2]=a[i];
else {
int pos=upper_bound(d+1,d+len2,a[i])-d;
d[pos]=a[i];
}
}
if(len1>=n-1||len2>=n-1){
printf("YES\n");
}
else printf("NO\n");
}
}
/*
6
5
1 4 3 4 5*/
/*
7
5
3 1 4 1 5
5
1 3 1 4 5
5
1 1 3 4 5
5
2 1 3 4 5
5
1 3 4 5 1
6
1 3 4 5 1 2
*/
这里的d真是奇妙~