zoukankan      html  css  js  c++  java
  • 2017级算法模拟上机准备篇(序列DP 初阶)

    序列DP 是一类蛮有意思的DP  序列区别于数组的地方就在于没有要求连续性

    从一道简单的序列DP开始:

    SkyLee炒股票(最大连续子序列和)

    这道题其实用DP有点浪费的感觉,其实这道题用简单的遍历法也可以,不过实质上还是DP思想的进阶。

    这道题如何设置DP数组?我们不妨设置DP[i]为以数组元素ar[i]结尾的连续序列的最大和.

    所以我们很容易得出状态转移方程:

    dp[i]=max(dp[i-1] + ar[i] , ar[i]);

    倘若之前的dp[i-1]再加上当前元素 得到的值更大,那么最大连续子序列的长度就会延长,如果没有那么就可以使其单个元素成为一个连续子序列。

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int Maxlen = 1e6 + 10;
    long long num[Maxlen];
    long long dp[Maxlen];//dp[i]代表的是以i元素结尾的连续子序列的最大值
    int main(int argc, char *argv[]) {
        
        int n,i,j,k;
        long long ans;
        while(~scanf("%d",&n)){
            for(i=1;i<=n;i++)
                scanf("%lld",&num[i]);
                dp[0]=0;
                ans=0;
            for(i=1;i<=n;i++){
                dp[i]=max(dp[i-1]+num[i],num[i]);
                ans=max(ans,dp[i]);
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }

    那如果我们要继续求解最大连续子序列和的起点和终点的位置呢?

    显然只需要利用dp数组进行一次搜索即可。

                if(dp[i]>ans){
                    ans=dp[i];
                    right=i;
                    left=i;
                    while(dp[left] > 0 && left >=1)
                        left--;
                    if(left ==0 || dp[left] < 0)
                        left++;
                }

     接下来再来求解一道经典的序列DP 问题 LIS

    ModricWang的导弹拦截系统(最长不上升子序列 LIS)

    这道题其实就是求解最长不上升子序列长度。

    我们首先思考DP数组的设置:dp[i]代表的是以数组元素ar[i]为结尾元素的不上升子序列长度。

    那转移方程呢?

    for(i=1;i<=n;i++){
            for(j=1;j<i;j++){
                if(ar[j]>=ar[i])
                    dp[i]=max(dp[j]+1,dp[i]);
            }
            ans=max(ans,dp[i]);
        }

    其实到这一步 就可以简单的总结一下序列DP的解法。

    做DP题的第一步就是合理的构造有确定含义 符合最优子结构 的DP数组

    然后是实现状态转移方程,其第一步的构思和第二部略有重复。最后实现一下DP数组的初始化,就可以完成了。

    序列DP的dp数组的设置通常具有的含义是 以给定数组ar[i]结尾的具有某种特殊含义的数组,然后继续思考状态转移方程即可。

    由于序列只有前后的逻辑关系,所以子问题和父问题,可以简单的理解为前后关系。

    同时序列DP的最值问题,通常在遍历求解DP数组的同时,更新即可。

    最长公共子序列(LCS)

    给定两个序列,求解最长公共子序列的问题。

    根据上述设置dp数组的思路 我们不妨设置 dp[i][j] 其中i为序列1 以下标i结尾 j为序列2 以下标j结尾

    状态转移方程:

     if(s1[i]==s2[j])
                    dp[i][j]=dp[i-1][j-1]+1;
                else
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);

    最长上升公共子序列(LCIS)

    http://acm.hdu.edu.cn/showproblem.php?pid=1423(原题链接)

    显然这道题是基与LCS的,那么如何求解上升的子问题,我们还是按照序列dp一贯的思路设置dp数组:

    设置 dp[i][j] 其中i为序列1 以下标i结尾 j为序列2 以下标j结尾 显然这些是不够的

    我们在此之上可以 添加一点条件:我们不妨设置dp[i][j]的公共子序列的末尾元素是dp[j]即:

    dp[i][j]表示以a串的前i个整数与b串的前j个整数且以b[j]为结尾构成的LCIS的长度

    那么我们来实现状态转移方程:

        if(a[i] != b[j])
            dp[i][j]=dp[i-1][j];
        else {
            for(k=1;k<=j-1;k++)
                if(b[j]>b[k])
                    dp[i][j]=max(dp[i][j],dp[i-1][k]+1);
        }

    从这道题我们可以总结出来序列DP的一些其他思想,如果存在两个以上变化的量,我们可以考虑以一个变量为标准,相当于确定了一个变量,这样思考另一个变量带来的影响更简洁。 

    中等·Bamboo's Fight with DDLs II (类似LCS的序列DP问题)

    dp[i][j]代表的是序列区间是从i到j的最长回文子序列长度

                    if(str[i] == str[j])
                        dp[i][j]=2+dp[i+1][j-1];
                    else dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
  • 相关阅读:
    web移动开发最佳实践之js篇
    ubuntu升级到12.10
    C语言生成随机数
    终于签约了
    这个2012不寻常
    awk练习(实战)
    数据恢复的教训
    职业发展的一些随想
    diy谷蜂Y5刷机包基于官方0207稳定版
    web移动开发最佳实践之html篇
  • 原文地址:https://www.cnblogs.com/visper/p/10118743.html
Copyright © 2011-2022 走看看