动态规划(一)最大最小值问题

本文主要是对动态规划(最大最小问题)的一个汇总

说明

类型说明

主要是根据一个target,查找抵达目标值的最小(大)成本/路径/总和

解题方法

在当前状态下之前的所以可能路径中选择最小(大)路径,然后再加上当前状态的值

routes[i] = min(routes[i-1], routes[i-2], ... , routes[i-k]) + cost[i]

为目标中的所有值生成最佳解决方案,并返回目标的值。

从上至下(记忆化搜索)

for (int j = 0; j < ways.size(); ++j) {
    result = min(result, topDown(target - ways[j]) + cost/ path / sum);
}
return memo[/*state parameters*/] = result;

自底而上(动态规划)

for (int i = 1; i <= target; ++i) {
   for (int j = 0; j < ways.size(); ++j) {
       if (ways[j] <= i) {
           dp[i] = min(dp[i], dp[i - ways[j]] + cost / path / sum) ;
       }
   }
}
return dp[target]

相关例题

例题汇总

编号 标题 难度 通过率
64 最小路径和 中等 69.30%
120 三角形最小路径和 中等 68.70%
174 地下城游戏 困难 48.60%
221 最大正方形 中等 49.10%
279 完全平方数 中等 65.40%
322 零钱兑换 中等 45.80%
474 一和零 中等 46.80%
570 只有两个键的键盘 中等 57.20%
746 使用最小花费爬楼梯 简单 62.60%
871 最低加油次数 困难 43.20%
931 下降路径最小和 中等 67.10%
983 最低票价 中等 63.40%
1049 最后一块石头的重量 II 中等 67.90%
1240 铺瓷砖 困难 50.60%

例题分析

最小路径和

// 最小路径求和
for(int i = 1; i < m; i++){
    for(int j = 1; j < n; j++){
        dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + grid[i][j];
    }
}

三角形最小路径和

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int m = triangle.size();
        int n = triangle[m-1].size();
        vector<vector<int>> dp(m, vector<int>(n, 0));
        dp[0][0] = triangle[0][0];
        // 进行数组元素的初始化
        for(int i = 1; i < m; i++){
            dp[i][0] = triangle[i][0] + dp[i-1][0];
            int j = triangle[i].size()-1;
            dp[i][j] = triangle[i][j] + dp[i-1][j-1];
        }

        // 进行状态转移的计算
        for(int i = 2; i < m; i++){
            for(int j = 1; j < triangle[i].size()-1; j++){
                dp[i][j] = min(dp[i-1][j] ,dp[i-1][j-1]) + triangle[i][j];
            }
        }

        // 遍历最后一层的最小元素
        int minimum = dp[m-1][0];
        for(int i = 1; i < n; i++){
            if(minimum > dp[m-1][i])
                minimum = dp[m-1][i];
        }

        return minimum;
    }
};

最大正方形

class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        vector<vector<int>> dp(m, vector<int>(n,0));

        // 计算最大面积
        int maxSide = 0;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(matrix[i][j] == '1'){
                    if(i == 0 || j == 0){
                        dp[i][j] = 1;
                    }else
                        dp[i][j] = min(min(dp[i][j-1], dp[i-1][j]), dp[i-1][j-1]) + 1;
                    maxSide = max(maxSide, dp[i][j]);
                }
            }
        }

        return maxSide * maxSide;
    }
};

完全平方数

解法几乎一样的题:零钱兑换最后一块石头的重量 II

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j * j <= i; j++) {
                dp[i] = min(dp[i], dp[i - j*j] + 1);
            }
        }
        return dp[n];
    }
};

零钱兑换

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1, INT_MAX);
        dp[0] = 0;
        for(int i = 1; i <= amount; i++){
            for(int j = 0; j < coins.size(); j++){
                if(coins[j] <= i)
                    dp[i] = min(dp[i], dp[i-coins[j]] + 1);
            }
        }
        return dp[amount] > amount ? -1 : dp[amount]; 
    }
};

最后一块石头的重量 II

class Solution {
public:
    int lastStoneWeightII(vector<int> &stones){
        int sum = accumulate(stones.begin(), stones.end(), 0);
        int target = sum / 2;
        vector<int> dp(target+1, 0);
        for(int i = 1; i <= target; i++){
            for(int j = 0; j < stones.size(); j++){
                if(i >= stones[j])
                    dp[i] = max(dp[i], dp[i-stones[j]] + stones[j]);
            }
        }
        return sum - 2 * dp[target];
    } 
};

一和零

class Solution {
public:
    vector<int> getZerosOnes(string& str) {
        vector<int> zerosOnes(2);
        int length = str.length();
        for (int i = 0; i < length; i++) {
            zerosOnes[str[i] - '0']++;
        }
        return zerosOnes;
    }

    int findMaxForm(vector<string>& strs, int m, int n) {
        int length = strs.size();
        vector<vector<vector<int>>> dp(length + 1, vector<vector<int>>(m + 1, vector<int>(n + 1)));

        // 进行状态转换计算
        for (int i = 1; i <= length; i++) {
            // 获取每个字符串的0和1的个数
            vector<int>&& zerosOnes = getZerosOnes(strs[i - 1]);
            int zeros = zerosOnes[0], ones = zerosOnes[1];
            // 状态转换
            for (int j = 0; j <= m; j++) {
                for (int k = 0; k <= n; k++) {
                    dp[i][j][k] = dp[i - 1][j][k];
                    if (j >= zeros && k >= ones) {
                        dp[i][j][k] = max(dp[i][j][k], dp[i - 1][j - zeros][k - ones] + 1);
                    }
                }
            }
        }
        return dp[length][m][n];
    }
};

只有两个键的键盘

class Solution {
public:
    int minSteps(int n) {
        //dp[i] 表示打印出 i 个A 的最少操作次数,显而易见,dp[0]=0,dp[1]=0
        vector<int> dp(n+1, 0);
        for (int i = 2; i <= n; i++){
            //每一个dp[i]最大值为i,即拷贝一次,粘贴i-1次
            dp[i] = i;
            for (int j = 2; j * j <= i; j++){
                if (i % j == 0) {
                    //题解中:拷贝一次,粘贴 i/j-1次正好是dp[i/j],拷贝一次,粘贴j-1次,正好是dp[j],直接一个式子搞定
                    dp[i] = dp[j] + dp[i / j];
                }
            }
        }
        return dp[n];
    }
};

下降路径最小和

class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        vector<vector<int>> dp(m, vector<int>(n));
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(i == 0){
                    dp[i][j] = matrix[i][j];
                }else if(j == 0){
                    dp[i][j] = min(dp[i-1][j], dp[i-1][j+1]) + matrix[i][j];
                }else if(j == n - 1){
                    dp[i][j] = min(dp[i-1][j], dp[i-1][j-1]) + matrix[i][j];
                }else
                    dp[i][j] = min(min(dp[i-1][j], dp[i-1][j-1]), dp[i-1][j+1]) + matrix[i][j];
            }
        }
        int minn = INT_MAX;
        for(int j = 0; j < n; j++){
            minn = min(minn, dp[m-1][j]);
        }
        return minn;
    }
};

最低票价

class Solution {
    unordered_set<int> dayset;
    vector<int> costs;
    int memo[366] = {0};

public:
    int mincostTickets(vector<int>& days, vector<int>& costs) {
        this->costs = costs;
        for (int d: days) {
            dayset.insert(d);
        }
        memset(memo, -1, sizeof(memo));
        return dp(1);
    }

    int dp(int i) {
        if (i > 365) {
            return 0;
        }
        if (memo[i] != -1) {
            return memo[i];
        }
        if (dayset.count(i)) {
            memo[i] = min(min(dp(i + 1) + costs[0], dp(i + 7) + costs[1]), dp(i + 30) + costs[2]);
        } else {
            memo[i] = dp(i + 1);
        }
        return memo[i];
    }
};

参考文章

posted @ 2022-08-02 11:01  車轱辘  阅读(248)  评论(0编辑  收藏  举报