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

Posted Dh_q

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最长上升子序列(LIS)动态规划相关的知识,希望对你有一定的参考价值。

最长上升子序列

  给你n个整数 AA2 ········· An 找出在这个数组里面的最长上升的子序列。例如给你(1,7,3,5,9,4,8),他的上升子序列有(1,7) (3,4,8)等等之类的,但是最长的上升子序列是(1,3,5,8)。

1)n^2算法

  dp[i] 为当前数组里第i个元素时,以a[i]元素为结尾的最长子序列长度。

  动态转移方程是:dp[i] = max ( dp[i] , dp[j] + 1 ) 其中 j<i 且 a[j] < a[i] 。

  

技术分享
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cstdlib>
 5 #include <string>
 6 #include <vector>
 7 #include <map>
 8 #include <set>
 9 #include <queue>
10 #include <sstream>
11 #include <algorithm>
12 using namespace std;
13 #define pb push_back
14 #define mp make_pair
15 #define ms(a, b)  memset((a), (b), sizeof(a))
16 //#define LOCAL
17 typedef long long LL;
18 const int inf = 0x3f3f3f3f;
19 const int maxn = 1000+10;
20 const LL mod = 1000000000+7;
21 int a[maxn];
22 int dp[maxn];
23 int main()
24 {
25     #ifdef LOCAL
26         freopen("input.txt" , "r", stdin);
27     #endif // LOCAL
28     int n;
29     scanf("%d", &n);
30     for(int i=0;i<n;i++)    scanf("%d", &a[i]);
31     dp[0]=1;
32     for(int i=1;i<n;i++)
33     {
34         dp[i] = 1;
35         for(int j=0;j<i;j++)
36         {
37             if(a[j]<a[i] && dp[j]+1 > dp[i])
38                 dp[i] = dp[j] + 1;
39         }
40     }
41     int ans = 0;
42     for(int i=0;i<n;i++)    ans = max(ans, dp[i]);
43     printf("%d\n", ans);
44     return 0;
45 }
View Code

 

2)nlogn算法

   

假设存在一个序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5。n
下面一步一步试着找出它。
我们定义一个序列B,然后令 i = 1 to 9 逐个考察这个序列。
此外,我们用一个变量Len来记录现在最长算到多少了

首先,把d[1]有序地放到B里,令B[1] = 2,就是说当只有1一个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1

然后,把d[2]有序地放到B里,令B[1] = 1,就是说长度为1的LIS的最小末尾是1,d[1]=2已经没用了,很容易理解吧。这时Len=1

接着,d[3] = 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候B[1..2] = 1, 5,Len=2

再来,d[4] = 3,它正好加在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时候B[1..2] = 1, 3,Len = 2

继续,d[5] = 6,它在3后面,因为B[2] = 3, 而6在3后面,于是很容易可以推知B[3] = 6, 这时B[1..3] = 1, 3, 6,还是很容易理解吧? Len = 3 了噢。

第6个, d[6] = 4,你看它在3和6之间,于是我们就可以把6替换掉,得到B[3] = 4。B[1..3] = 1, 3, 4, Len继续等于3

第7个, d[7] = 8,它很大,比4大,嗯。于是B[4] = 8。Len变成4了

第8个, d[8] = 9,得到B[5] = 9,嗯。Len继续增大,到5了。

最后一个, d[9] = 7,它在B[3] = 4和B[4] = 8之间,所以我们知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。

于是我们知道了LIS的长度为5。

!!!!! 注意。这个1,3,4,7,9不是LIS,它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。虽然最后一个d[9] = 7更新进去对于这组数据没有什么意义,但是如果后面再出现两个数字 8 和 9,那么就可以把8更新到d[5], 9更新到d[6],得出LIS的长度为6。

然后应该发现一件事情了:在B中插入数据是有序的,而且是进行替换而不需要挪动——也就是说,我们可以使用二分查找,将每一个数字的插入时间优化到O(logN)~~~~~于是算法的时间复杂度就降低到了O(NlogN)~!

技术分享
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cstdlib>
 5 #include <string>
 6 #include <vector>
 7 #include <map>
 8 #include <set>
 9 #include <queue>
10 #include <sstream>
11 #include <algorithm>
12 using namespace std;
13 #define pb push_back
14 #define mp make_pair
15 #define ms(a, b)  memset((a), (b), sizeof(a))
16 //#define LOCAL
17 typedef long long LL;
18 const int inf = 0x3f3f3f3f;
19 const int maxn = 50000+10;
20 const LL mod = 1000000000+7;
21 int a[maxn];
22 int dp[maxn];
23 int Binary_search(int key, int len)
24 {
25     int l=1, r=len+1;
26     while(l<r)
27     {
28         int middle = (l+r) >> 1;
29         if(key>=dp[middle])
30             l = middle +1;
31         else
32             r = middle;
33     }
34     return l;
35 }
36 int main()
37 {
38     #ifdef LOCAL
39         freopen("input.txt" , "r", stdin);
40     #endif // LOCAL
41         int n, len;
42         scanf("%d", &n);
43         for(int i=0;i<n;i++)    scanf("%d", &a[i]);
44         dp[1] = a[0];
45         len = 1;
46         for(int i=1;i<n;i++)
47         {
48             if(a[i] > dp[len])
49                 dp[++len] = a[i];
50             else{
51                 int j = Binary_search(a[i], len);
52                 dp[j] = a[i];
53             }
54         }
55         printf("%d\n", len);
56     return 0;
57 }
View Code

 

附上一些练习题:

1)https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1134

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

3)http://poj.org/problem?id=1631

4)http://poj.org/problem?id=1887(最长不上升子序列)

5)http://poj.org/problem?id=1609(最长不上升子序列)

这些题的题解稍后放出。




以上是关于最长上升子序列(LIS)动态规划的主要内容,如果未能解决你的问题,请参考以下文章

LIS 最长上升子序列问题(动态规划贪心+二分)

LIS 最长上升子序列问题(动态规划贪心+二分)

动态规划解决最长上升子序列LIS避坑

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

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

luogu1439 模板最长公共子序列 [动态规划][LIS最长上升子序列][离散化]