1275: God‘s ladder [DP]
时间限制: 1 Sec 内存限制: 128 MB Special Judge
题目描述
天明来到神之宫殿,在他眼前出现了若干个石柱,每个石柱上有1枚金币,天明可以任意选择一个石柱开始,然后向前方的石柱瞬移,而且他所瞬移到的石柱的高度必须要大于现在所在石柱的高度。
求天明所能获得的最大金币数以及任意一种可以获得这么多金币的路线(每个石柱的高度)。
输入
第一行一个数n,表示石柱的个数。
然后2~n+1行,每行一个石柱的高度h[i],分别是1,2,,n石柱的高度。
2<n<200100;1<= h[i] < 15000000;
输出
第一行为一个数m,表示最大金币数。
2~m+1行,每行一个数,分别是每次所瞬移到的石柱的高度。
样例输入
7
3
1
2
5
9
6
7
样例输出
5
1
2
5
6
7
代码:
#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)且小于此元素的原宿存在。