题意:给出长度为n的序列,要求你删除掉一段的连续子序列,使得剩下的序列的递增子序列最长
分析:
书上讲解的很详细,摘一段:
最容易想到的算法是枚举j和i(前提是A[j]< A[i],否则拼不起来),然后分别往左和往 右数一数最远能延伸到哪里。枚举量为O(n2),而“数一数”的时间复杂度为O(n),因此总时间 复杂度为O(n3)。
加上一个预处理,就能避免“数一数”这个过程,从而把时间复杂度降为O(n2)。设f(i)为 以第i个元素开头的最长L序列长度,g(i)为以第i个元素结尾的最长L序列长度,则不难在O(n) 时间内求出f(i)和g(i),然后枚举完j和i之后,最长L序列的长度就是g(j)+f(i)。
还可以做得更好:只枚举i,不枚举j,而是用其他方法快速找一个j< i,使得A[j]< A[i], 且g(j)尽量大。如何快速找到呢?首先要排除一些肯定不是最优值的j。例如,若有j’满足A[j’] <=A[j]且g(j’)> g(j),则j肯定不满足条件,因为j’不仅是一个更长的L序列的末尾,而且它更容 易拼成。
这样,把所有“有保留价值”的j按照A[j]从小到大排成一个有序表(根据刚才的结 论,A[j]相同的j只保留一个),则g也会是从小到大排列。那么用二分查找找到满足A[j]< A[i] 的最大的A[j],则它对应的g(j)也是最大的。
不过这个方法只有当i固定时才有效。实际上每次计算完一个g(i)之后,还要把这个A[i] 加到上述有序表中,并且删除不可能是最优的A[j]。因为这个有序表会动态变化,无法使用 排序加二分查找的办法,而只能使用特殊的数据结构来满足要求。幸运的是,STL中的set就 满足这个要求——set中的元素可以看成是排好序的,而且自带lower_bound和upper_bound函 数,作用和之前讨论过的一样。
#include<cstdio>
#include<set>
#include<algorithm>
using namespace std;
const int N=2e5+9;
#define a first
#define l second
#define mp make_pair
typedef pair<int,int>pii;
int l[N],r[N],g[N],a[N];
set<pii>s;
int main()
{
//freopen("f.txt","r",stdin);
int T;scanf("%d",&T);
while(T--){
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
l[1]=1;
for(int i=2;i<=n;i++)l[i]=a[i-1]<a[i]?l[i-1]+1:1;
r[n]=1;
for(int i=n-1;i>=1;i--)r[i]=a[i]<a[i+1]?r[i+1]+1:1;
s.clear();
s.insert(pii(a[1],l[1]));
int ans=1;
for(int i=2;i<=n;i++){
pii t=mp(a[i],l[i]);
set<pii>::iterator it=s.lower_bound(t);
bool keep=1;
if(it!=s.begin()){
pii last=*(--it);
ans=max(ans,last.l+r[i]);
if(last.l>=t.l)keep=0;
}
if(keep){
s.erase(t);
s.insert(t);
it=s.find(t);
++it;
while(it!=s.end()&&it->a >t.a&&it->l <=t.l)s.erase(it++);
}
}
printf("%d
",ans);
}
return 0;
}
这题还是LIS的变形,用LIS的nlogn的算法就可以解决,思想上和上面的分析一样,LIS稍微变化即可。其中g保存的是最优的a[i]的值(且它的l[i]值是最优的),可以看出g中保存的值是递增的。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2e5+9;
#define INF 0x3f3f3f3f;
int l[N],r[N],g[N],a[N];
int main()
{
//freopen("f.txt","r",stdin);
int T;scanf("%d",&T);
while(T--){
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
l[1]=1;
for(int i=2;i<=n;i++)l[i]=a[i-1]<a[i]?l[i-1]+1:1;
r[n]=1;
for(int i=n-1;i>=1;i--)r[i]=a[i]<a[i+1]?r[i+1]+1:1;
for(int i=0;i<=n;i++)g[i]=INF;
int ans=0;
for(int i=1;i<=n;i++){
int len=lower_bound(g+1,g+1+n,a[i])-(g+1)+r[i];
ans=max(ans,len);
g[l[i]]=min(a[i],g[l[i]]);
}
printf("%d
",ans);
}
return 0;
}