2 5 2 1 2 3 4 5 5 3 5 4 3 2 1
Case #1: 3 Case #2: 1
假设最长子序列在被删除位置的起点的左边,即满足 i < n - L
那么答案就是【0,i】中i可以排到的最长的位置
假设删除区间在最长子序列中间部分
枚举每一个点x,删除位置为[x-l-1,x-1]。
那么计算出[0,x-l-1]的最长上升序列的情况,然后看第x个数能加入这些序列最长的位置y。那么答案就是
y + x能够出现在[x,n]区间中以x开头的最长子序列的长度(相当于逆序求x所在最长下降子序列的的长度)。
证明如下:
对于一个x,删除[x-l-1,x-1]的位置得到的结果最优。
显然对于x来说得到的结果是[0,x-l-1]+[x,n]包含x的最长子序列长度。
那么可能的情况是 存在y,y的位置在[x-l-1,x-1]区间,且x,y同时存在这个最长的子序列结果中。
那么显然在[y,n]区间x会被计算到关于y必选的子序列的情况中。并且关于y的最长的子序列的长度已经在之前计算得到。
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> using namespace std; #define maxn 100007 int dp[maxn]; int rs[maxn],ls[maxn]; int num[maxn]; int find(int n,int val){ int low = 0,high = n-1; while(low <= high){ int mid = (low+high)/2; if(dp[mid] >= val) high = mid-1; else low = mid+1; } return low; } int main(){ int t,n,l,tt=1; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&l); for(int i = 0;i < n; i++) scanf("%d",&num[i]); int cnt = 0; for(int i = n-1;i >= 0; i--){ int p = find(cnt,-num[i]); dp[p] = -num[i]; rs[i] = p; if(p == cnt) cnt++; } cnt = 0; int ans = 0; memset(ls,0,sizeof(ls)); for(int i = 0;i < n; i++){ if(i+l < n){ int p = find(cnt,num[i+l]); ls[i+l] = p; } int p = find(cnt,num[i]); dp[p] = num[i]; if(p == cnt) cnt++; if(i < n - l) ans = max(ans,p+1); } for(int i = l;i < n; i++){ ans = max(ans,rs[i]+ls[i]+1); } printf("Case #%d: %d\n",tt++,ans); } return 0; }