LIS 最长上升子序列 多种解法

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

最长上升子序列分严格上升或者不严格上升,我写的代码是严格上升的,也就是说前后不可以相等 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;
}

树状树组

树状树组的话就感觉太过于繁琐了

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值