【动态规划】线性DP【未完。。。】

刷题指南

最经典的单串

最长上升子序列(LIS)

分析:时间复杂度O( n 2 n^{2} n2)

状态表示:dp[i] : 以i为结尾的最长上升子序列长度。
状态计算:dp[i] = dp[j] + 1, (nums[j]  > nums[i])
	答案: dp[i]的最大值
class Solution {
    
    
public:
    int lengthOfLIS(vector<int>& nums) {
    
    
        int n = nums.size();
        vector<int> dp(n, 1);
        
        for(int i = 0;i < n;i ++)
            for(int j =0;j < i ;j ++ )
            {
    
    
                if(nums[i] > nums[j]) dp[i] = max(dp[i],dp[j] + 1);
            }
        int res = 0;
        for(int i = 0;i < n;i ++ ) res = max(res,dp[i]);
        return res;
    }
};

896. 最长上升子序列 II(贪心 + 二分)

分析:时间复杂度O( n l o g n nlogn nlogn)

状态表示:q[i] :长度为i的上升子序列末尾元素的最小值
状态计算:已证明q[]严格单调递增,每次只需要二分找到nums[i]可以接到q[]数组后面,即q[x] < nums[i],
				长度为x + 1,更新q[x + 1] = nums[i]
class Solution {
    
    
public:
    int lengthOfLIS(vector<int>& nums) {
    
    
        int n = nums.size();
        
        vector<int> q(n + 1);
        int len = 0;
        for(int i =0 ; i< n;i ++ )
        {
    
    
            int l = 0, r = len;
            while(l < r)
            {
    
    
                int mid = l + r  + 1 >> 1;
                if(q[mid] < nums[i]) l = mid;
                else r = mid - 1;
            }
            len = max(len,r + 1);
            q[r + 1] = nums[i];
        }
        return len;
    }
};

最经典的双串

最长公共子序列

分析:时间复杂度O( n 2 n^{2} n2)

状态表示:dp[i][j] : A[1~i]与B[1~j]的最长公共子序列长度
状态计算:dp[i][j] = max(dp[i-1][j], dp[i][j - 1]);
		   dp[i][j] = dp[i - 1][j - 1] (A[i] == B[j]);
class Solution {
    
    
public:
    int longestCommonSubsequence(string s1, string s2) {
    
    
        int n1 = s1.size(), n2 = s2.size();
        vector<vector<int>> dp(n1 + 1,vector<int>(n2 + 1));

        for(int i = 1;i <= n1;i ++ )
            for(int j = 1;j <= n2;j ++ )
            {
    
    
                dp[i][j] = max(dp[i-1][j], dp[i][j - 1]);
                if(s1[i - 1] == s2[j - 1]) dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1); // 下标从1开始
            }
        
        return dp[n1][n2];
    }
};

数字三角形

分析:

状态表示:dp[i][j]: 	到下标为[i,j]的最短路径和
状态计算:dp[i][j] = min(dp[i-1][j],dp[i-1][j-1]) + nums[i][j],注意边界!
class Solution {
    
    
public:
    int minimumTotal(vector<vector<int>>& triangle) {
    
    
        int n = triangle.size();
        vector<vector<int>> dp(n,vector<int>(n));

        dp[0][0] = triangle[0][0];
        for(int i = 1;i < n ;i ++ )
            for(int j = 0;j <= i; j ++ )
            {
    
    
                dp[i][j] = INT_MAX;
                if(j < i) dp[i][j] =min(dp[i][j],dp[i - 1][j] + triangle[i][j]); // 一定要加j < i,不然溢出,因为上一层少一个元素
                if(j >= 1) dp[i][j] = min(dp[i][j],dp[i-1][j-1] + triangle[i][j]);
            }
        
        int res = INT_MAX;
        for(int i = 0;i < n;i ++) res = min(res,dp[n - 1][i]);
        return res;
    }
};

省空间的做法:&1,只需要保存上一层的状态

class Solution {
    
    
public:
    int minimumTotal(vector<vector<int>>& nums) {
    
    
        int n = nums.size();
        vector<vector<long long>> f(2,vector<long long>(n));
        f[0][0] = nums[0][0];

        for(int i=1;i<n;i++)
            for(int j=0;j<=i;j++)
            {
    
    
                f[i & 1][j] = INT_MAX;
                if(j > 0) f[i & 1][j] = min(f[i & 1][j],f[i-1 & 1][j-1] + nums[i][j]);
                if(j < i) f[i & 1][j] = min(f[i & 1][j],f[i-1 & 1][j] + nums[i][j]);
            }
        
        long long res = INT_MAX;
        for(int i=0;i<n;i++) res = min(res,f[n-1 & 1][i]);
        return res;
    }
};

猜你喜欢

转载自blog.csdn.net/weixin_43154149/article/details/112321448