JHHK

欢迎来到我的个人网站
行者常至 为者常成

【题目】6、动态规划

目录

47. 礼物的最大价值

题目描述

在一个 m*n 的棋盘的每一格都放有一个礼物,
 每个礼物都有一定的价值(价值大于0)。
 你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。
 给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

 示例 1:
 输入:
 [
   [1,3,1],
   [1,5,1],
   [4,2,1]
 ]
 输出: 12
 解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物

 来源:力扣(LeetCode)
 链接:https://leetcode-cn.com/problems/li-wu-de-zui-da-jie-zhi-lcof
 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目实现一

class Solution1 {
public:
    int maxValue(vector<vector<int>>& grid) {
        
        int rowLen = (int)grid.size();
        int colLen = (int)grid[0].size();
        
        //dp[i][j] 表示走到 grid[i-1][j-i]位置拿到的最大礼物价值
        vector<vector<int>> dp(rowLen+1,vector<int>(colLen+1,0));
        
        //状态转移方程
        //dp[i][j] = max(dp[j][i-1] dp[j-1][i]) + grid[i-1][j-1]
        
        for (int row = 1; row<=rowLen; row++) {
            for (int col = 1; col<=colLen; col++) {
                int value1 = dp[row-1][col];
                int value2 = dp[row][col-1];
                dp[row][col] = max(value1, value2) + grid[row-1][col-1];
            }
        }
        
        return dp[rowLen][colLen];
    }
};

题目实现二

class Solution2 {
public:
    int maxValue(vector<vector<int>>& grid) {
        
        int rowLen = (int)grid.size();
        int colLen = (int)grid[0].size();
                
        for (int row = 1; row<=rowLen; row++) {
            for (int col = 1; col<=colLen; col++) {
                int value1 = row-2<0?0:grid[row-2][col-1];
                int value2 = col-2<0?0:grid[row-1][col-2];
                grid[row-1][col-1] = max(value1, value2) + grid[row-1][col-1];
            }
        }
        
        return grid[rowLen-1][colLen-1];
    }
};

题目实现三

class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        
        int rowLen = (int)grid.size();
        int colLen = (int)grid[0].size();
        
        vector<int> dp(colLen+1,0);
        
        //状态转移方程
        //dp[i][j] = max(dp[j][i-1] dp[j-1][i]) + grid[i-1][j-1]
        
        for (int row = 1; row<=rowLen; row++) {
            for (int col = 1; col<=colLen; col++) {
                int value1 = dp[col];
                int value2 = dp[col-1];
                dp[col] = max(value1, value2) + grid[row-1][col-1];
            }
        }
        
        return dp[colLen];
    }
};

64. 最小路径和

题目描述

64. 最小路径和
 
 给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

 说明:每次只能向下或者向右移动一步。

 示例 1:
 输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
 输出:7
 解释:因为路径 1→3→1→1→1 的总和最小。
 
 示例 2:
 输入:grid = [[1,2,3],[4,5,6]]
 输出:12
  

 提示:
 m == grid.length
 n == grid[i].length
 1 <= m, n <= 200
 0 <= grid[i][j] <= 100

 来源:力扣(LeetCode)
 链接:https://leetcode-cn.com/problems/minimum-path-sum
 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目实现

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        
        int rowLen = (int)grid.size();
        int colLen = (int)grid[0].size();
        
        vector<int>dp(colLen+1,INT_MAX);
        
        for (int row = 1; row<=rowLen; row++) {
            for (int col = 1; col<=colLen; col++) {
                if (row == 1 && col == 1) {
                    dp[col] = grid[0][0];
                    continue;
                }
                int value1 = dp[col-1];
                int value2 = dp[col];
                dp[col] = min(value1, value2)+grid[row-1][col-1];
            }
        }
        return dp[colLen];
    }
};

62. 不同路径

题目描述

62. 不同路径
 
 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
 问总共有多少条不同的路径?


 例如,上图是一个7 x 3 的网格。有多少可能的路径?
  

 示例 1:
 输入: m = 3, n = 2
 输出: 3
 解释:
 从左上角开始,总共有 3 条路径可以到达右下角。
 1. 向右 -> 向右 -> 向下
 2. 向右 -> 向下 -> 向右
 3. 向下 -> 向右 -> 向右
 
 示例 2:
 输入: m = 7, n = 3
 输出: 28


 提示:
 1 <= m, n <= 100
 题目数据保证答案小于等于 2 * 10 ^ 9

 来源:力扣(LeetCode)
 链接:https://leetcode-cn.com/problems/unique-paths
 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目实现

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<int> dp(n,1);
        for (int row = 1; row<m; row++) {
            for (int col = 1; col<n; col++) {
                dp[col] = dp[col] + dp[col-1];
            }
        }
        return dp[n-1];
    }
};

121. 买卖股票的最佳时机

题目描述

 121. 买卖股票的最佳时机
 
 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

 如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

 注意:你不能在买入股票前卖出股票。


 示例 1:
 输入: [7,1,5,3,6,4]
 输出: 5
 解释:
 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
 
 示例 2:
 输入: [7,6,4,3,1]
 输出: 0
 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

 来源:力扣(LeetCode)
 链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目实现-方式一

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        
        if (prices.size() == 0) return 0;
        
        int minPrice = prices[0];
        int maxValue = 0;
        for (int i = 1; i<prices.size(); i++) {
            if (prices[i] < minPrice) {
                minPrice = prices[i];
            }else{
                maxValue = max(maxValue, prices[i] - minPrice);
            }
        }
        return maxValue;
    }
};

题目实现-方式二

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        
        if (prices.size() == 0) return 0;
        
        for (int i = 1; i<prices.size(); i++) {
            prices[i-1] = prices[i] - prices[i-1];
        }
        prices[prices.size()-1] = 0;
        
        return maxContinueSubSeqSum(prices);
    }
    
    //计算最大连续子序列和
    int maxContinueSubSeqSum(vector<int>& price){
        //dp[i] 以price[i]结尾的最大连续子序列和
        vector<int> dp(price.size());
        dp[0] = price[0];
        int maxValue = dp[0];
        for (int i = 1; i<price.size(); i++) {
            if (dp[i-1] > 0) {
                dp[i] = dp[i-1] + price[i];
            }else{
                dp[i] = price[i];
            }
            maxValue = max(maxValue,dp[i]);
        }
        
        return maxValue;
    }
};

72. 编辑距离

题目描述

72. 编辑距离
 
 给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

 你可以对一个单词进行如下三种操作:
 插入一个字符
 删除一个字符
 替换一个字符
  

 示例 1:
 输入:word1 = "horse", word2 = "ros"
 输出:3
 解释:
 horse -> rorse (将 'h' 替换为 'r')
 rorse -> rose (删除 'r')
 rose -> ros (删除 'e')
 
 示例 2:
 输入:word1 = "intention", word2 = "execution"
 输出:5
 解释:
 intention -> inention (删除 't')
 inention -> enention (将 'i' 替换为 'e')
 enention -> exention (将 'n' 替换为 'x')
 exention -> exection (将 'n' 替换为 'c')
 exection -> execution (插入 'u')
  

 提示:
 0 <= word1.length, word2.length <= 500
 word1 和 word2 由小写英文字母组成

 来源:力扣(LeetCode)
 链接:https://leetcode-cn.com/problems/edit-distance
 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目实现-方式一

class Solution {
public:
    int minDistance(string word1, string word2) {
        
        int w1Len = (int)word1.length();
        int w2Len = (int)word2.length();
        if (w1Len == 0 || w2Len == 0) return w1Len>w2Len?w1Len:w2Len;
        
        //dp[i][j] word1[0,i-1] 到 word2[0,j-1]的最小操作步数
        vector<vector<int>> dp(w1Len+1,vector<int>(w2Len+1,0));
        
        for (int i = 0; i<=w1Len; i++) {
            dp[i][0] = i;
        }
        
        for (int j = 0; j<=w2Len; j++) {
            dp[0][j] = j;
        }
        
        for (int row = 1; row<=w1Len; row++) {
            for (int col = 1; col<=w2Len; col++) {
                int value1 = dp[row-1][col]+1;
                int value2 = dp[row][col-1]+1;
                int value3 = dp[row-1][col-1];
                if (word1[row-1] != word2[col-1]) value3+=1;
                dp[row][col] = min(min(value1,value2),value3);
            }
        }
        
        return  dp[w1Len][w2Len];
    }
};

题目实现-方式二

class Solution {
public:
    int minDistance(string word1, string word2) {
        
        int w1Len = (int)word1.length();
        int w2Len = (int)word2.length();
        if (w1Len == 0 || w2Len == 0) return w1Len>w2Len?w1Len:w2Len;
        
        //dp[i][j] word1[0,i-1] 到 word2[0,j-1]的最小操作步数
        //优化成一维数组
        vector<int> dp(w2Len+1,0);
        
        for (int j = 0; j<=w2Len; j++) {
            dp[j] = j;
        }
        
        for (int row = 1; row<=w1Len; row++) {
            int cur = dp[0];
            dp[0] = row;
            for (int col = 1; col<=w2Len; col++) {
                int value1 = dp[col]+1;
                int value2 = dp[col-1]+1;
                int value3 = cur;
                cur = dp[col];
                if (word1[row-1] != word2[col-1]) value3+=1;
                dp[col] = min(min(value1,value2),value3);
            }
        }
        
        return  dp[w2Len];
    }
};

5. 最长回文子串

题目描述

5. 最长回文子串
 
 给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

 示例 1:

 输入: "babad"
 输出: "bab"
 注意: "aba" 也是一个有效答案。
 示例 2:

 输入: "cbbd"
 输出: "bb"

 来源:力扣(LeetCode)
 链接:https://leetcode-cn.com/problems/longest-palindromic-substring
 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目实现 - 暴力法

class Solution1 {
public:
    string longestPalindrome(string s) {
        int length = (int)s.length();
        if (length == 0 || s.compare("") == 0) return s;
        
        int left = 0;
        int maxLength = 1;
        
        for (int i = 0; i<length; i++) {
            for (int j = i+1; j<length; j++) {
                if (isPalindrom(s, i, j) && maxLength < j - i + 1) {
                    maxLength =j - i + 1;
                    left = i;
                }
            }
        }
        return s.substr(left,maxLength);
    }
    
    bool isPalindrom(string s,int left,int right){
        while (left < right) {
            if (s[left++] == s[right--]) continue;
            return false;
        }
        return true;
    }
};

题目实现 - 动态规划

class Solution2 {
public:
    string longestPalindrome(string s) {
        int length = (int)s.length();
        if (length <= 1 ) return s;
        
        // 最长回文子串的长度(至少是1)
        int maxLength = 1;
        
        // 最长回文子串的开始索引
        int left = 0;
        
        //dp[i][j] 代表s[i][j] 是否是回文串
        vector<vector<bool>> dp(length,vector<bool>(length,true));
        
        // 从下到上(i由大到小)
        for (int i = length-1; i>=0; i--) {
            // 从左到右(j由小到大)
            for (int j = i; j<length; j++) {
                
                // cs[i, j]的长度
                int len = j - i + 1;
                dp[i][j] = (s[i] == s[j] && (len<=2 || dp[i+1][j-1]));
                
                if (dp[i][j] && maxLength < len){// 说明cs[i, j]是回文子串
                    left = i;
                    maxLength = len;
                }
            }
        }
        
        return s.substr(left,maxLength);
    }
};

题目实现 - 扩展中心法

class Solution3 {
public:
    //扩展中心法
    string longestPalindrome(string s) {
        int length = (int)s.length();
        if (length <= 1 ) return s;
        
        // 最长回文子串的长度(至少是1)
        int maxLen = 1;
        // 最长回文子串的开始索引
        int begin = 0;
        
        for (int i = length - 2; i >= 1; i--) {
            // 以字符为中心向左右扩展
            int len1 = palindromeLength(s, i - 1, i + 1);
            // 以字符右边的间隙为中心向左右扩展
            int len2 = palindromeLength(s, i, i + 1);
            len1 =max(len1, len2);
            if (len1 > maxLen) {
                maxLen = len1;
                begin = i - ((maxLen - 1) >> 1);
            }
        }
        // 以0号字符右边的间隙为中心的最长回文子串长度是2
        if (s[0] == s[1] && maxLen < 2) {
            // cs[0, 1]就是最长的回文子串
            begin = 0;
            maxLen = 2;
        }
        
        return s.substr(begin,maxLen);
    }
    
    /**
     * @return 从l开始向左、从r开始向右扫描,获得的最长回文子串的长度
     */
    int palindromeLength(string cs, int l, int r) {
        while (l >= 0 && r < cs.length() && cs[l] == cs[r]) {
            l--;
            r++;
        }
        return r - l - 1;
    }
};

题目实现 - 扩展中心法2

class Solution {
public:
    //扩展中心法2
    string longestPalindrome(string s) {
        int length = (int)s.length();
        if (length <= 1 ) return s;
        
        // 最长回文子串的长度(至少是1)
        int maxLen = 1;
        // 最长回文子串的开始索引
        int begin = 0;
        int i = 0;
        while (i < length) {
            int l = i - 1;
            // 找到右边第一个不等于s[i]的位置
            int r = i;
            while (++r < length && s[r] == s[i]);
            // r会成为新的i
            i = r;
            
            // 从l向左,从r向右扩展
            while (l >= 0 && r < length && s[l] == s[r]) {
                l--;
                r++;
            }
            
            // 扩展结束后,cs[l + 1, r)就是刚才找到的最大回文子串
            // ++l后,l就是刚才找到的最大回文子串的开始索引
            int len = r - ++l;
            if (len > maxLen) {
                maxLen = len;
                begin = l;
            }
        }
        
        return s.substr(begin,maxLen);
    }
};

题目实现 - 马拉车法


行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.