最长上升子序列分严格上升或者不严格上升,我写的代码是严格上升的,也就是说前后不可以相等 1 1 1 1 不算上升序列,和不严格上升相差一个的等号,其实没什么差别,思想是一样的。
DP
最基础的是dp,复杂度为O(n^2),虽然说我觉得没什么dp的影子。
dp[i],表示选第i个数,且以第i个数为结尾的最长上升子序列为多少,状态转移方程为dp[i]=max(dp[k]), (1<=k<i&&a[k]<=a[i])。
核心代码如下:
while(~scanf("%d", &n)){
rep(i,1,n) a[i]=read(), dp[i]=0;
rep(i,1,n){
int mx = 0;
rep(j,1,i){
if(a[j]<=a[i]) mx = max(mx, dp[j]);
}
dp[i]=mx+1;
}
int ans = 0;
rep(i,1,n) ans = max(dp[i], ans);
printf("%d\n", ans);
}
dp果然暴力
二分
思想就是设一个数组b[i]来记录上升为i时能够得到的最小的数是多少,举个例子 1 3 2 5 4
len表示b数组的长度
首先a[1]进来,b[1] = 1,序列为1
——>a[2]进来, b[2] = 3, 序列为 1 3
——>a[3]进来, b[2] = 2, 序列为 1 2
——>a[4]进来, b[3] = 5, 序列为 1 2 5
——>a[5]进来, b[3] = 4。 序列为 1 2 4
最后得到最长上升子序列为len=3
而且可以看出数组b是一个上升的数组,所以可以用二分来进行查找第一个大于等于的数,然后进行替换即可
代码如下:
int a[maxn], b[maxn]; //b数组记录的是上升子序列长度为i时最小的数
signed main(){
int n;
while(~scanf("%d", &n)){
rep(i,1,n) a[i]=read(), b[i]=0;
int len=0; //b[i]数组的长度,最开始为零
rep(i,1,n){
int j = lower_bound(b+1, b+len+1, a[i])-b; //二分查找在b中大于等于a[i]的位置,然后进行更新
b[j]=a[i]; //注意这里是必定会进行更新,因为要么是直接放到最后面要么就是,在中间插入而且b[j]>=a[i],所以可以进行更新
if(j>=len+1) len++; //算一个剪枝把,可以直接用n的但是如果用len的话就是最后的答案
//printf("%d %d\n", j, len);
}
printf("%d\n", len);
}
return 0;
}
树状树组
树状树组的话就感觉太过于繁琐了

本文深入探讨了最长上升子序列问题的三种解决方案:动态规划、二分查找优化的动态规划及树状数组方法。详细介绍了每种方法的实现思路与核心代码,对比了它们的时间复杂度与效率。
1638

被折叠的 条评论
为什么被折叠?



