算法训练1-day32-动态规划

25 阅读1分钟

**1. 322. 零钱兑换

dp数组定义:coins数组构成j的组合所需的最少硬币个数 是求组合数,因此先物品后背包 求最小值,初始化为INT_MAX-1,另外dp[0]根据定义设置为0

AC代码:

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

322. 零钱兑换类似,物品就是各个平方数,即:1,22,33,44....1,2^2,3^3,4^4.... 注意i的平方可以等于n,也就是n恰好是平方数的情况

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

        return dp[n];
    }
};
  1. 139. 单词拆分

每个单词都可以使用多次,且不要求顺序,因此是排列问题,先背包后物品

注意递推条件,除了看子串是否与wordDict[i]相等以及前面的j-k位置是否能构成外,当不使用第i个单词也能构成目标串时,即使当前第i个单词不符合条件,因为之前有符合要求的排列组合,因此此时dp[j]也是为true

代码如下:

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        int n = wordDict.size();
        int m = s.size();

        // 以wordDict[i]单词结尾,是否能构成字符串s[0...j-1]
        vector<bool> dp(m + 1, false);
        dp[0] = true;

        for (int j = 1; j <= m; ++j) {
            for (int i = 0; i < n; ++i) {
                int k = wordDict[i].size();
                if (j < k) {
                    continue;
                }

                dp[j] = dp[j] || (dp[j - k] && (wordDict[i] == s.substr(j - k, k)));
            }
        }

        return dp[m];
    }
};