zoukankan      html  css  js  c++  java
  • 【动态规划】最长上升子序列(LIS)

    今天看了《挑战程序设计竞赛》的动态规划部分,感觉对以前一些知其然却不知其所以然的问题有了更好的理解,先整理一部分。

    题意:

    有一个长为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;
    }
    
  • 相关阅读:
    如何用Matplotlib绘制三元函数
    总结一下在新工作中都学到了什么?
    Debian MySQL 卸载和安装 PHP安装
    Sphinx的配置和使用
    Python的多继承
    任务分配准则
    Python解析XMl
    什么是序列化,Python中json的load,loads,dump,dumps和pickle的load,loads,dump,dumps的区别
    程序文件路径和目录的操作之BASEDIR目录获取
    模块和包
  • 原文地址:https://www.cnblogs.com/Tuesdayzz/p/5758836.html
Copyright © 2011-2022 走看看