300分钟学习-动态规划

动态规划的定义

  • 一种数学优化的方法,同时也是编程的方法

重要属性

  • 最优子结构 Optimal Substructure
    状态转移方程
  • 重叠子问题 Overlapping Sub-problems
    有的子问题结果会被多次用到
    在这里插入图片描述
    一个简单的例子/。动态规划的要点之一就是找子问题,这里可以分解为从A到B和从B到C两个子问题。 这两个子问题是比较容易求得的。但问题是题目要求不能重复,所以这个题不能这样找子结构。

在这里插入图片描述

例:leetcode300 最长子序列的长度
在这里插入图片描述
要考虑的其实就是如何根据f(1)~f(n-1)得到f(n)
在这里插入图片描述

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> cache(nums.size(),1);//下标为i的代表以这个结尾的最长子串大小
        int maxNum=0;

        for(int i=0;i<nums.size();i++){
            for(int j=0;j<i;j++){
                //如果发现nums[j]<nums[i],说明nums[i]有机会构成上升序列
                if(nums[i]>nums[j]&&cache[j]+1>cache[i]){
                    cache[i]=cache[j]+1;
                }
            }
            maxNum=max(maxNum,cache[i]);
        }
        return maxNum;
    }
};

线性规划

  • 各个子问题的规模以线性的方式分布
  • 子问题的最佳状态或结果可以存储在一维线性的数据结构中,例如一维数组,哈希表等。
  • 通常会用dp[i]表示第i个位置的结果,或者从0开始到第i个位置为止的最佳状态或结果。

基本形式:

  • 当前所求的值仅仅依赖于有限个先前计算好的值,即dp[i]仅仅依赖于有限个dp[j] (j<i)。
    例1“求斐波那契数列时,dp[i]=dp[i-1]+dp[i-2] leetcode 70

例题2:leetcode 198 给定一个数组,不能选择相邻的数,求如何才能使总数最大(0 1思想)
设dp[i]为到第i个时搜到的金额总大小。
dp[i]=max(dp[i-2]+nums[i],dp[i-1])第i个房子选还是不选对应两种情况,取两种情况下的较大值

class Solution {
public:
    int rob(vector<int>& nums) {
        int n= nums.size();
        if(n==0)return 0;
        if(n==1)return nums[0];

        vector <int> dp(n,-1);
        dp[0]=nums[0];
        dp[1]=max(nums[0],nums[1]);

        for(int i=2;i<n;i++){
            dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
        }
        return dp[n-1];
    }
};

leetcode62
在这里插入图片描述
设dp[i][j]为走到(i,j)坐标位置的路径个数,由于只能往右或者往下走,得到dp[i][j]=dp[i-1][j]+dp[i][j-1]其实就是填一个矩阵。最上和最左两个向量都是1。

class Solution {
public:
    int uniquePaths(int m, int n) {
        if(m==1||n==1)return 1;
        if(m==0||n==0)return 0;

        vector<vector<int>> dp(m,vector<int>(n,-1));
        dp[0][0]=0;
        for(int i=1;i<n;i++)dp[0][i]=1;
        for(int j=1;j<m;j++)dp[j][0]=1;
        for(int p=1;p<m;p++){
            for(int q=1;q<n;q++){
                dp[p][q]=dp[p-1][q]+dp[p][q-1];
            }
        }
        return dp[m-1][n-1];
    }
};

还有一种情况是当前所求的值依赖于所有先前计算和的值,即dp[i]是各个do[j]的某种组合,其中j由0遍历到i-1
例:在求最长上升子序列时,dp[i]=max(dp[j]+1)+1 0<=j<i

区间规划

  • 各个子问题的规模由不同区间来定义
  • 子问题的最佳状态或结果可以存在二维数组中
  • 这类问题的时间复杂度一般为多项式时间

例:leetcode 516 最长回文子序列
在这里插入图片描述
dp[i][j]为i到j区间内的最长回文
递推:
首尾字符相等:dp[0][n-1]=dp[1][n-2]+2
否则:dp[0][n-1]=max(dp[1][n-1],dp[0][n-2])
也是类似填矩阵,要先填对角线。因为这个题是从中间往两边填的

class Solution {
public:
    int longestPalindromeSubseq(string s) {
         int n=s.size();
         vector<vector<int>> dp(n,vector<int>(n,-1));
         for(int i=0;i<n;i++) dp[i][i]=1;

         for(int len=2;len<=n;len++){
             for(int i=0;i<n-len+1;i++){
                 int j=i+len-1;

                 if(s[i]==s[j]){
                     dp[i][j]=2+(len==2?0:dp[i+1][j-1]);
                 }else{
                     dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
                 }
             }
         }
         return dp[0][n-1];
    }
};

猜你喜欢

转载自blog.csdn.net/weixin_42189888/article/details/105286103