【动态规划】最长上升子序列(LIS)
2016-02-01 21:46
351 查看
今天看了《挑战程序设计竞赛》的动态规划部分,感觉对以前一些知其然却不知其所以然的问题有了更好的理解,先整理一部分。
题意:
有一个长为n的数列a0,a1,a2,...,an 。请求出这个序列中最长的上升子序列的长度。上升子序列指的是对于任意的i<j都满足ai<aj的子序列。分析:
设dp[i]为第i个下标之前的子串中最长上升子序列长度。得到递推关系式,时间复杂度O(n2)。dp[i] = max(dp[i], dp[j] + 1) (a[i] > a[j])
代码:
#include<iostream> using namespace std; int a[105], dp[105]; int main (void) { int n, ans = 0; cin>>n; for (int i = 0; i < n; i++) cin>>a[i]; fill(dp, dp + n, 1); for (int i = 0; i < n; i++){ for (int j = 0; j < i; j++){ if(a[i]>a[j]) dp[i] = max (dp[i], dp[j] + 1); } ans = max (dp[i], ans); } cout<<ans<<endl; return 0; }
分析:
还可以定义dp[i]为长度为i的上升子序列中末尾元素的最小值,对于长度相同的子序列,末尾元素越小,最终获得的上升子序列越可能长。从序列头开始对每个元素a[j]考虑上升子序列长度为0...i的情况,得到递推关系式,时间复杂度O(n2)。dp[i] = min(dp[i], a[j]) (a[j]>dp[i-1]||i==1)
代码:
#include<iostream> using namespace std; const int INF=0x3fffffff; int a[105], dp[105]; int main (void) { int n, ans = 0; cin>>n; for (int i = 0; i < n; i++) cin>>a[i]; fill(dp + 1, dp + n + 1, INF); dp[0] = a[0]; for(int j = 0; j < n; j++){ for(int i = 1; i <= n; i++){ if(i == 1||a[j] > dp[i-1]) dp[i] = min(dp[i], a[j]); } } for(int i = n; i >= 1; i--){ if(dp[i] != INF){ cout<<i<<endl; break; } } return 0; }
分析:
上述方法中dp数组除INF外单调递增,所以对于每个元素,最多只更新一次dp数组, 而对于这次更新的位置,不必挨个遍历,可以直接二分查找下界,将复杂度降到O(logn)。代码:
#include<iostream> using namespace std; const int INF=0x3fffffff; int a[105], dp[105]; int _binary_search(int l, int r, int num) { while(l < r){ //区间[l,r) int mid = l + (r - l)/2; if(dp[mid] >= num) r = mid; else l = mid + 1; } return l; }//获取下界 int main (void) { int n, ans = 0; cin>>n; for (int i = 0; i < n; i++) cin>>a[i]; fill(dp + 1, dp + n + 1, INF); for(int j= 1; j <= n; j++){ int pos = _binary_search(1, n+1, a[j]); dp[pos] = a[j]; } for(int i = n; i >= 1; i--){ if(dp[i] != INF){ cout<<i<<endl; break; } } return 0; }
分析:
可以直接使用STL中的lower_bound获取下界。通过dp数组中INF的下界获取上升子序列长度。为方便表示可直接定义dp[i]为长度为i+1的上升子序列中末尾元素的最小值。有关 lower_bound 之前写过些简单介绍。代码:
#include<iostream> using namespace std; const int INF=0x3fffffff; int a[105], dp[105]; int main (void) { int n, ans = 0; cin>>n; fill(dp, dp + n, INF); for (int i = 0; i < n; i++){ cin>>a[i]; *lower_bound(dp, dp + n, a[i]) = a[i]; } cout<<lower_bound(dp, dp +n, INF) - dp<<endl; return 0; }
相关文章推荐
- C++动态规划之最长公子序列实例
- C++动态规划之背包问题解决方法
- C#使用动态规划解决0-1背包问题实例分析
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- LFC1.0.0 版本发布
- 动态规划
- C++ 动态规划
- Android px、dp、sp之间相互转换
- HP data protector软件学习1--基本角色与基本工作流程
- HP data protector软件学习2--软件组成与界面介绍
- android中像素单位dp、px、pt、sp的比较
- DP(动态规划) 解游轮费用问题
- Android对px和dip进行尺寸转换的方法
- 动态规划的用法——01背包问题
- 动态规划的用法——01背包问题
- 《收集苹果》 动态规划入门
- 《DNA比对》蓝桥杯复赛试题
- 《背包问题》 动态规划
- 初学ACM - 半数集(Half Set)问题 NOJ 1010 / FOJ 1207