动态规划 最长上升子序列(LIS)

本文介绍了如何使用动态规划解决最长上升子序列(LIS)问题,包括O(N^2)和两种不同的O(NlogN)解法,并提供了算法思想的理解链接及两道相关练习题。

O(N2)写法:

memset(dp, 0, sizeof(dp))

for(i = 0; i < n; i++) {

         dp[i]= 1;

         for(j= 0; j < i; j++) {

                   if(s[j]< s[i]) dp[i] = max(dp[i], dp[j] + 1);

         }

}

O(nlogn)写法1:( dp[i]存长度为i + 1的上升子序列中末尾元素的最小值)

fill(dp, dp + n, inf);

for(i = 0; i < n; i++) {

         *lower_bound(dp,dp + n, a[i]) = a[i];

}

printf(“%d\n”, lower_bound(dp, dp + n, inf)– dp);

 

O(nlogn)写法2:dp[i]存长度为i的上升子序列中末尾元素的最小值,下标从1开始

         intk = 1;

         b[1]= a[1];

         for(i= 2; i <= n; i++) {

                   if(a[i]> b[k]) {

                            b[++k]= a[i];

                   }

                   else{

                            char*pos = lower_bound(b, b + k,a[i]);

                            *pos= a[i];

                   }

         }

         printf("%d\n",k);


理解算法思想:http://blog.youkuaiyun.com/joylnwang/article/details/6766317


几个题目:

nyoj 17 :http://acm.nyist.net/JudgeOnline/problem.php?pid=17

怎么写都可以过


poj 3903 http://poj.org/problem?id=3903

poj 2533 http://poj.org/problem?id=2533

两道LIS裸题,nlogn 都可以过

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int a[100100], b[100100];
int main() {
	int n;
	while(~scanf("%d", &n)) {
		int i, j;
		for(i = 0; i < n; i++) {
			scanf("%d", a + i);
		}
		int k = 0;
		b[0] = a[0];
		for(i = 1; i < n; i++) {
			if(a[i] > b[k]) {
				b[++k] = a[i];
			}
			else {
				*lower_bound(b, b + k, a[i]) = a[i];
			}
		}
		printf("%d\n", k + 1);
	}
	return 0;
}


### 动态规划解决最长上升子序列问题 #### 什么是动态规划动态规划是一种通过分解复杂问题为更简单的子问题来解决问题的方法[^3]。它通常用于优化问题,其中解决方案可以通过组合最优的局部解得到全局最优解。 #### 最长上升子序列 (LIS) 的定义 最长上升子序列是指在一个给定数组中找到的一个最小子序列,该子序列中的元素按顺序严格递增[^2]。这个问题可以使用动态规划的思想高效地解决。 #### 使用动态规划求解 LIS 的两种方法 ##### 方法一:O() 解法 这种方法的核心思想是利用一个辅助数组 `dp` 来记录以每个位置结尾的最大长度 LIS。具体实现如下: ```python def length_of_LIS(nums): if not nums: return 0 dp = [1] * len(nums) for i in range(1, len(nums)): for j in range(i): if nums[i] > nums[j]: dp[i] = max(dp[i], dp[j] + 1) return max(dp) # 测试代码 nums = [10, 9, 2, 5, 3, 7, 101, 18] print(length_of_LIS(nums)) # 输出应为 4 ``` 此方法的时间复杂度为 O(),适用于较小规模的数据集。 ##### 方法二:O(n log n) 解法 为了提高效率,我们可以采用一种基于贪心和二分查找的技术。核心思路是维护一个列表 `sub`,表示当前可能构成的最小末尾值的 LIS 序列。 ```python import bisect def length_of_LIS_optimized(nums): sub = [] for num in nums: pos = bisect.bisect_left(sub, num) if pos == len(sub): sub.append(num) else: sub[pos] = num return len(sub) # 测试代码 nums = [10, 9, 2, 5, 3, 7, 101, 18] print(length_of_LIS_optimized(nums)) # 输出应为 4 ``` 这种改进版本的时间复杂度降到了 O(n log n)[^4],适合处理大规模数据输入。 #### 总结 上述两种方法展示了如何应用动态规划技术去计算最长上升子序列的长度。对于实际编程练习或者竞赛环境下的快速开发需求来说,推荐掌握这两种不同时间复杂性的算法实现方式。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值