zoukankan      html  css  js  c++  java
  • 最长上升子序列(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
    ", 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
    ", 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(最长不上升子序列)

    这些题的题解稍后放出。

  • 相关阅读:
    练习三
    练习四
    练习二
    软件生命周期
    练习一 第六题
    练习一 第五题
    练习一 第四题
    练习一 第三题
    练习一 第二题
    AngularJs模块
  • 原文地址:https://www.cnblogs.com/denghaiquan/p/6679952.html
Copyright © 2011-2022 走看看