首先由poj一道模板题引入
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
int num[100005];//序列
int pos[100005];//长度为i的子序列结尾的元素值
int dp[100005];
int main()
{
int n;
while(cin>>n){
for(int i=1;i<=n;i++)
cin>>num[i];
for(int i=0;i<=n;i++)
pos[i]= 1e9+5;
int ans=0;
for(int i=1;i<=n;i++){
int k=lower_bound(pos+1,pos+n+1,num[i])-pos;
dp[i] = k;
pos[k] = num[i];
ans = max(ans,k);
}
cout<<ans<<endl;
}
return 0;
}
pos[i]记录长度为k的上升子序列的结尾元素的值,num中存储了这个序列的值,从1开始到n,依次判断结尾为num[i]时,这个最长上升子序列有多少个元素(k个元素),然后替换pos[k]为num[i]
(即如果有多个子序列满足长度为k时,pos[k]中应该存储的是结尾最小的那个元素。而从1到n使用二分依次判断大于num[i]的第一个元素所在的位置,则将那个大于num[i]的元素替换为num[i],就能得到一个上升子序列,而且在相同的子序列长度中,这个子序列的结尾的元素必定最小(自行证明)。)
最后,pos数组满足pos[i]!=INF的最大的索引即为最长上升子序列的长度。如果要输出这个序列,只需要由pos[ans]所在的元素位置往前搜索符合递减规律的点即可。
下面是一道类似LIS的题,求的是相邻的值的差值小于d的一个最长的子序列,可以借用LIS的思路来做这题。
因为题目给的p[i]的值在10^5的范围内,因此我们可以用pos变量来存储pos[p[i]] = j,j是p[i]在序列中的位置。然后用DP的思路来做即可。
#include<bits/stdc++.h>
using namespace std;
int num[300005];
int dp[300005];
int pos[100005];
int main()
{
int T;
cin>>T;
while(T--){
int n,d;
for(int i=0;i<300001;i++){
num[i]=0;
dp[i]=0;
}
for(int i=0;i<100001;i++)
pos[i]=0;
cin>>n>>d;
for(int i=1;i<=n;i++)
cin>>num[i];
int ans=1;
for(int i=1;i<=n;i++){
int l = max(1,num[i]-d);
int r =min(100000,num[i]+d);
for(int j=l;j<=r;j++){
if(!pos[j]) continue;
dp[i] = max(dp[i],dp[pos[j]]+1);
}
ans = max(ans,dp[i]);
pos[num[i]] = i;
}
cout<<ans+1<<endl;
}
return 0;
}