这个问题源于线性代数中的问题。
问题描述:设一个整数序列为A,A={a0,a1,a2,......, an}。其中任意一个递增子序列为Sk,Sk={ai,....,aj,......}, 其中ai,aj为Ak序列中任意两个元素且i<j。ai<aj。A的所有递增子序列集合为S(Sk),找出最长的Sk,即得到了最长递增子序列的长度。
举个例子,7,3,6,5,7 它的最长递增子序列为3,5,7。长度为3。
LIS问题动态规划解决方法
算法:取A中的元素ai,计算以ai结尾的必须包含ai的最长子递增序列L(i)。遍历序列A,得到 L(i)的集合L。从L中找出最长的元素L(i),即为最长的递增子序列。
解释:设最长的递增子序列为M,则M一定会包含一个A的元素。因为M是递增的,所以M中最后一个元素M(last)是M中最大的元素。M以M(last)为ai,以ai结尾的必须包含ai的最长子递增序列”所以M必在L中。
抽象理解太难,从具体例子中发现算法。以例子说话
索引 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
整数序列 |
65 |
158 |
170 |
155 |
239 |
300 |
207 |
389 |
以ai结尾字符最大递增子序列长度 |
1 |
2 |
3 |
2 |
4 |
5 |
4 |
6 |
最长递增子序列,简称LIS。
最大递增子序列长度,简称LOLIS
a[1]的LOLLS为2。
a[2]的LOLLS为3
现在的问题是求a[4]的LOLLS。
一种原始的求LOLLS的方法是,找出所有以a[4]结尾的递增子序列,找出其中最长的那个子序列。
S1 = 65 239
S2 = 65 158 239
S3 = 65 170 239
S4 = 65 155 239
S5 = 65 158 170 239
Si = ...........
我们可以得到a[i]的LOLLS为4。
这种算法会搞死计算机的。即使用以后用量子计算机也无济于事。
如果能利用以前的计算结果,可能会提高计算速度。我们重新描述一下寻找以a[j]结尾的最长递增子序列长度问题。
先分解这个问题为一个小问题:
设元素a[i], i<j,求子序列s(i) = {a0,a1,....,a[i],a[j]}的最长递增子序列。
算法描述:如果a[i] < a[j],则a[j]的LOLIS为a[i]的LOLIS +1
如果a[i] > a[j],则遍历ai之前对应的每一个LIS,找出最长的那个LIS,a[j]
的LOLIS,则为最长的那个LIS+1
寻找以a[j]结尾的最长递增子序列长度问题的算法,实际上是重复调用上面的小问题的算法就可以了。
算法描述:寻找以a[i]结尾的LIS(i),i={0, ........., n}。找出LIS(i)中最大的那个LIS。
为了实现方便,可以变换一下算法的顺序:如果a[i] > a[j],,则遍历ai之前对应的每一个LIS,找出最长的那个LIS,然后加1。
代码如下
int MyGetLIS(int a[], int n) { int * b = new int[n]; memset(b, 0, sizeof(int)*n); b[0] = 1; vector<int> intVec; for (int i=1; i<n; ++i) { int k = 0; intVec.clear(); for (int j = 0; j< i; ++j) { if (a[i] > a[j] ) { intVec.push_back(b[j]); } } k = *max_element(intVec.begin(), intVec.end()); b[i] = k+1; } for (int i=0; i<n; ++i) { cout << b[i] << endl; } cout << "Length Of LIS = " << *max_element(b, b+n) << endl; delete [] b; return 0; }
只要俏加改动,便动得到网络上随处可见的那段求LIS问题的经典代码了。
代码如下,
int GetLIS(int a[], int n) { int * b = new int[n]; memset(b, 0, sizeof(int)*n); for (int i=0; i<n; ++i) { int k = 0; for (int j=0; j<i; ++j) { if (a[i]>a[j] && k<b[j]) { k = b[j]; } } b[i] = k + 1; } int m = *max_element(b, b+n); delete [] b; return m; }
完整代码如下
#include <iostream> #include <algorithm> #include <vector> using namespace std; bool myfn(int * i, int * j) { return (*i)<(*j); } int GetLIS(int a[], int n) { int * b = new int[n]; memset(b, 0, sizeof(int)*n); for (int i=0; i<n; ++i) { int k = 0; for (int j=0; j<i; ++j) { if (a[i]>a[j] && k<b[j]) { k = b[j]; } } //重用计算结果 b[i] = k + 1; } int m = *max_element(b, b+n); delete [] b; return m; } int MyGetLIS(int a[], int n) { int * b = new int[n]; memset(b, 0, sizeof(int)*n); b[0] = 1; vector<int> intVec; for (int i=1; i<n; ++i) { int k = 0; intVec.clear(); for (int j = 0; j< i; ++j) { if (a[i] > a[j] ) { intVec.push_back(b[j]); } } k = *max_element(intVec.begin(), intVec.end()); b[i] = k+1; } for (int i=0; i<n; ++i) { cout << b[i] << endl; } cout << "Length Of LIS = " << *max_element(b, b+n) << endl; delete [] b; return 0; } int main() { int a[]={65, 158, 170, 155, 239, 300, 207, 389};//8---5 cout << GetLIS(a, 8) << endl; MyGetLIS(a, 8); cin.get(); return 0; }