天明来到神之宫殿,在他眼前出现了若干个石柱,每个石柱上有1枚金币,天明可以任意选择一个石柱开始,然后向前方的石柱瞬移,而且他所瞬移到的石柱的高度必须要大于现在所在石柱的高度。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define inf 0x3f3f3f3f
int dp[200220],list[200220],a[200220];//dp[i]保存lis为i时最小的元素,list保存每个元素的lis;
int main()
{
int n,i,j,k,p;
while (cin>>n){p=0;
memset(dp,inf,sizeof(dp));
for(i=0;i<n;i++){
scanf("%d",&a[i]);
*lower_bound(dp,dp+n,a[i])=a[i]; //记录更新长度对应的最大潜力元素时,顺便记录下该元素对应的LIS长度
list[p++]=lower_bound(dp,dp+n,a[i])-dp+1;
}
k=lower_bound(dp,dp+n,inf)-dp; //总的LIS
cout<<k<<endl;
int m=k;
for(i=n-1;i>=0;i--){ //从右至左扫描一遍直至找全LIS长度
if(k==list[i])
dp[k--]=a[i];
if(!k) break;
}
for(i=1;i<=m;i++)printf("%d\n",dp[i]);
}
return 0;
}
更快的解法,减小二分搜索的次数:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define inf 0x3f3f3f3f
int dp[200220],list[200220],a[200220];
int main()
{
int n,i,j,k,p;
while (cin>>n){p=0;k=0;
memset(dp,inf,sizeof(dp));
for(i=0;i<n;i++){
scanf("%d",&a[i]);
if(a[i]>dp[k]) dp[++k]=a[i]; // 设置k为当前最长lis值
else*lower_bound(dp,dp+k,a[i])=a[i];
list[p++]=lower_bound(dp,dp+k,a[i])-dp+1;
}
// k=lower_bound(dp,dp+n,inf)-dp;
k=k+1;
printf("%d\n",k);
int m=k;
for(i=n-1;i>=0;i--){
if(k==list[i])
dp[k--]=a[i];
if(!k) break;
}
for(i=1;i<=m;i++)printf("%d\n",dp[i]);
}
return 0;
}
从右至左扫描的必要性:
由于是求单调上升子序列,所以最大值定在右侧。
又具有相同lis的不同元素中,潜力最大的也在右侧。
即假设一个元素的lis为n(n>2),则在这个元素之前定有lis为(n-1)且小于此元素的原宿存在。