专题训练之 DP

参考:

动态规划的那些套路

动态规划的那些套路2

 

1.Leetcode1186[最大子数组变形]

class Solution {
public:
    int maximumSum(vector<int>& arr) {
        int n = arr.size();
        vector<vector<int>> dp(n, vector<int>(2, 0));
        dp[0][0] = arr[0];
        int res = arr[0];
        for (int i = 1; i < n; i++) {
            dp[i][0] = max(arr[i], dp[i-1][0]+arr[i]);
            dp[i][1] = max(dp[i-1][1]+arr[i], dp[i-1][0]);
            res = max(res, max(dp[i][0], dp[i][1]));
        }
        return res;
    }
};
leetcode1186

子数组中最多可以去掉一个数,求最大子数组。需要维护两个不同的状态,表示到当前为止,是否去掉了一个数了。 

 

2.Leetcode198

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if (n == 0) return 0;
        else if (n == 1) return nums[0];
        else if (n == 2) return max(nums[0], nums[1]);
        vector<int> dp(n, 0);
        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];
    }
};
leetcode198

* 选不选第 i 个位置的值

 

3.Leetcode213

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if (n == 0) return 0;
        else if (n == 1) return nums[0];
        else if (n == 2) return max(nums[0], nums[1]);
        vector<int> dp(n, 0);
        dp[0] = nums[0];
        dp[1] = max(nums[1], nums[0]);
        int res = max(dp[0], dp[1]);
        for (int i = 2; i < n-1; i++) {
            dp[i] = max(dp[i-1], dp[i-2]+nums[i]);
            res = max(res, dp[i]);
        }
        dp[1] = nums[1];
        dp[2] = max(nums[1], nums[2]);
        for (int i = 3; i < n; i++) {
            dp[i] = max(dp[i-1], dp[i-2]+nums[i]);
            res = max(res, dp[i]);
        }
        return res;
    }
};
leetcode213

上一题的变形,增加了一个条件:不能同时选取头/ 尾的数。计算两次,一次不选头,一次不选尾部。

 

4.Best Time to Buy and Sell Stock

A.Leetcode121:买一次,卖一次。贪心即可,维持当前访问到的数中的最小值

B.Leetcode122:可以多次交易,求利润最大。贪心即可,当访问的位置比前一个位置的值大时,便可加上其差值。

C.Leetcode123:可以参与两次交易,求利润最大。暴力一点的做法是枚举的分割给定的数组,将其分为两部分,然后求各自的最大,将结果相加,不断更新这个结果。维护两个数组,dp1[i] 表示从 [0, i] 这个范围内进行一次交易最大的利润,问题简化成 leetcode121。dp2[i] 表示从 [i, n-1] 这个范围内的最大的利润。然后从头到尾扫一遍,枚举分割点。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if (n < 2) return 0;
        vector<int> dp1(n, 0), dp2(n, 0);
        int minx = prices[0];
        int ans = 0;
        for (int i = 1; i < n; i++) {
            int val = prices[i] - minx;
            dp1[i] = max(dp1[i-1], val);
            minx = min(minx, prices[i]);
            ans = max(ans, dp1[i]);
        }
        int maxx = prices[n-1];
        for (int i = n-2; i > 0; i--) {
            int val = maxx - prices[i];
            dp2[i] = max(dp2[i+1], val);
            maxx = max(maxx, prices[i]);
            ans = max(ans, dp2[i] + dp1[i-1]);
        }
        return ans;
    }
};
leetcode123

D.Leetcode188:最多进行 K 次交易,求利润最大。设置两个数组,buy[i] 表示第 i 次买时,当前最大的利润是多少。sell[i] 表示第 i 次卖时,当前最大的利润是多少。buy[i] = max(buy[i], sell[i-1]-nums[j]),sell[i] = max(sell[i], buy[i]+nums[j])。

class Solution {
public:
    int maxProfit(int k, vector<int>& nums) {
        int n = nums.size(), ans = 0;
        if (k >= n/2) {
            for (int i = 1; i < n; i++) {
                if (nums[i] > nums[i-1]) ans += nums[i]-nums[i-1];
            }
            return ans;
        }
        vector<int> buy(k+1, INT_MIN), sell(k+1, 0);
        for (int i = 0; i < n; i++) {
            for (int j = 1; j <= k; j++) {
                buy[j] = max(buy[j], sell[j-1]-nums[i]);
                sell[j] = max(sell[j], buy[j]+nums[i]);
            }
        }
        return sell[k];
    }
};
leetcode188

 E.Leetcode309:可以进行任意次数的交易,但是存在冷冻期,当你在第 i 天卖出手头的股票,不能立刻在第 i+1 天买入新的股票。设置两个数组 buy[i] 表示在 [0, i] 的范围内,手上含有股票的最大利润是多少。 sell[i] 表示在 [0, i] 的范围内,手上没有股票的最大的利润是多少。sell[i] = max(sell[i-1], max(buy[j] + prices[i]))(其中 j < i)。buy[i] =  max(buy[i-1], sell[i-1]+prices[i])。

 1 class Solution {
 2 public:
 3     int maxProfit(vector<int>& prices) {
 4         int n = prices.size();
 5         if (n == 0) return 0;
 6         vector<int> buy(n+1, INT_MIN), sell(n, 0); 
 7         for (int i = 0; i < n; i++) {
 8             if (i != 0) sell[i] = sell[i-1];
 9             for (int j = i-1; j >= 0; j--) {
10                 sell[i] = max(sell[i], buy[j] + prices[i]); 
11             }
12             if (i == 0) buy[i] = -prices[i];
13             else if (i == 1) buy[i] = max(-prices[i], -prices[i-1]);
14             else buy[i] = max(buy[i-1], sell[i-2] - prices[i]);
15         }
16         return sell[n-1];
17     }
18 };
leetcode309

 

5.Leetcode689

class Solution {
public:
    vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> sum(n+1, 0), dp(3, 0), pos[3];
        int sm = 0;
        for (int i = 0; i < n; i++) {
            sm += nums[i];
            if (i >= k-1) {
                sum[i] = sm;
                sm -= nums[i-k+1];
                if (i >= 3*k-1) {
                    if (sum[i-2*k] > dp[0]) {
                        dp[0] = sum[i-2*k];
                        pos[0] = {i-2*k};
                    }
                    if (sum[i-k] + dp[0] > dp[1]) {
                        dp[1] = sum[i-k] + dp[0];
                        pos[1] = {pos[0][0], i-k};
                    }
                    if (sum[i] + dp[1] > dp[2]) {
                        dp[2] = sum[i] + dp[1];
                        pos[2] = {pos[1][0], pos[1][1], i};
                    }
                }    
            }
        }
        return {pos[2][0]-k+1,pos[2][1]-k+1,pos[2][2]-k+1};
    }
};
leetcode689

给定一个数组,从数组中取出三个不相交,且各自元素数目为 k 的子数组,求三个子数组的最大和。

设置 sum[i],表示在 [i-k+1, i] 这个范围内子数组的和。dp[0] 表示一个满足条件的子数组的最大和,对应 pos[0] 表示 dp[0] 所对应子数组的位置.

详细解释可以见:https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231/C%2B%2BJava-DP-with-explanation-O(n)

https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108246/C%2B%2B-O(n)-time-O(n)-space-concise-solution

 

6.Leetcode300

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> dp;
        int n = nums.size();
        for (int i = 0; i < n; i++) {
            int num = nums[i];
            auto iter = lower_bound(dp.begin(), dp.end(), num);
            if (iter == dp.end()) dp.push_back(num);
            else if (num < *iter) *iter = num;
        }
        return dp.size();
    }
};
leetcode300

求最长上升子串

解法1(time: N^2):dp[i] 表示以 i 结尾满足条件的最长上升子串。dp[i] = max(dp[i], dp[j]+1) (0<=j<i && nums[j] < nums[i])

解法2(time: Nlog(n)):用 dp 数组保存满足条件的最长上升子串。i 从 0 开始访问,若 dp 数组中没有比 nums[i] 大的元素,则将 nums[i] 加入到 dp 数组的尾部。若有,则找到第一个比 nums[i] 大的元素,用 nums[i] 进行替换。最后 dp.size() 便是所求的答案

 

7.Leetcode368

class Solution {
public:
    vector<int> largestDivisibleSubset(vector<int>& nums) {
        vector<int> res;
        int n = nums.size();
        if (n == 0) return res;
        sort(nums.begin(), nums.end());
        vector<int> dp(n, 1), pre(n);
        for (int i = 0; i < n; i++) {
            pre[i] = i;
            for (int j = i-1; j >= 0; j--) {
                if (nums[i] % nums[j] == 0 && dp[j]+1>dp[i]) {
                    dp[i] = dp[j]+1;
                    pre[i] = j;
                }
            }
        }
        int pos, ans = 0;
        for (int i = 0; i < n; i++) {
            if (dp[i] > ans) {
                ans = dp[i];
                pos = i;
            }
        }
        while (pre[pos] != pos) {
            res.push_back(nums[pos]);
            pos = pre[pos];
        }
        res.push_back(nums[pos]);
        reverse(res.begin(), res.end());
        return res;
    }
};
leetcode368

求满足条件的最长子串

思路同上一题的解法1

 

8.Leetcode673

class Solution {
public:
    int findNumberOfLIS(vector<int>& nums) {
        int n = nums.size();
        if (n == 0) return 0;
        vector<int> dp(n, 1), cnt(n, 1);
        for (int i = 0; i < n; i++) {
            for (int j = i-1; j >= 0; j--) {
                if (nums[i] > nums[j]) {
                    if (dp[i] == dp[j]+1) cnt[i] += cnt[j];
                    else if (dp[i] < dp[j]+1) {
                        dp[i] = dp[j] + 1;
                        cnt[i] = cnt[j];
                    }
                }
            }
        }
        int ans = 0, res = 0;
        for (int i = 0; i < n; i++) {
            if (dp[i] > ans) {
                ans = dp[i];
                res = cnt[i];
            } else if (dp[i] == ans) {
                res += cnt[i];
            }
        }
        return res;
    }
};
leetcode673

第 6 题得到变形,求最长上升子串的个数。在第 6 题的基础上需要额外记录数量。

 

9.Leetcode1105

class Solution {
public:
    int minHeightShelves(vector<vector<int>>& books, int shelf_width) {
        int n = books.size();
        vector<int> dp(n+1, INT_MAX);
        dp[0] = 0;
        dp[1] = books[0][1];
        for (int i = 1; i < n; i++) {
            int maxx = books[i][1];
            int width = books[i][0];
            dp[i+1] = dp[i] + maxx;
            for (int j = i-1; j >= 0; j--) {
                if (width+books[j][0] > shelf_width) break;
                maxx = max(maxx, books[j][1]);
                width += books[j][0];
                dp[i+1] = min(dp[i+1], dp[j] + maxx);
            }
        }
        return dp[n];
    }
};
leetcode1105

按顺序将书放入书架,给定书架的宽度,求书架的最小高度。

dp[i] 表示考虑了前 i 本书,满足条件时书架的最小高度。dp[i] = min(dp[i], dp[j] + maxx) (表示从 [j+1, i] 位置的书放到新的一层,maxx 表示这些书中最高的那个值)

 

10.Leetcode1143

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int n = text1.size(), m = text2.size();
        vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (text1[i] == text2[j]) {
                    dp[i+1][j+1] = max(dp[i+1][j+1], dp[i][j]+1);
                } else {
                    dp[i+1][j+1] = max(dp[i+1][j+1], max(dp[i+1][j], dp[i][j+1]));
                }
            }
        }
        return dp[n][m];
    }
};
leetcode1143

给定两个字符串,求最长公共子串。

 

11.Leetcode1092

class Solution {
public:
    string shortestCommonSupersequence(string& A, string& B) {
        int i = 0, j = 0;
        string res = "";
        for (char c : lcs(A, B)) {
            while (A[i] != c)
                res += A[i++];
            while (B[j] != c)
                res += B[j++];
            res += c, i++, j++;
        }
        return res + A.substr(i) + B.substr(j);
    }

    string lcs(string& A, string& B) {
        int n = A.size(), m = B.size();
        vector<vector<string>> dp(n + 1, vector<string>(m + 1, ""));
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                if (A[i] == B[j])
                    dp[i + 1][j + 1] = dp[i][j] + A[i];
                else
                    dp[i + 1][j + 1] = dp[i + 1][j].size() > dp[i][j + 1].size() ?  dp[i + 1][j] : dp[i][j + 1];
        return dp[n][m];
    }
};
leetcode1092

给定两个字符串,求一个长度最短的字符串,使得给定的两个字符串为该字符串的子串。

类似于上面的方法,先求出最长的公共子串,然后补足剩下没有被公共子串所涵盖的部分。

 

12.Leetcode72

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n = word1.size(), m = word2.size();
        vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
        for (int i = 1; i <= m; i++) dp[0][i] = i;
        for (int i = 1; i <= n; i++) dp[i][0] = i;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (word1[i] == word2[j]) dp[i+1][j+1] = dp[i][j];
                else {
                    dp[i+1][j+1] = min(min(dp[i+1][j]+1, dp[i][j+1]+1), dp[i][j]+1);
                }
            }
        }
        return dp[n][m];
    }
};
leetcode72

只能进行单个字符的增加、删除和替换,求把字符串 word1 变成字符串 word2 最少需要几次操作

设 dp[i][j] 表示 word1[0, i) -> word2[0, j) 最少需要的变换次数

 

13.Leetcode115

class Solution {
public:
    int numDistinct(string s, string t) {
        int n = s.size(), m = t.size();
        vector<vector<long long>> dp(n+1, vector<long long>(m+1, 0));
        for (int i = 0; i <= n; i++) dp[i][0] = 1;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (s[i] == t[j]) {
                    dp[i+1][j+1] = dp[i][j] + dp[i][j+1];
                } else {
                    dp[i+1][j+1] = dp[i][j+1];
                }
            }
        }
        return (int)dp[n][m];
    }
};
leetcode115(解法1)
class Solution {
public:
    int numDistinct(string s, string t) {
        int m = t.size();
        vector<long long> dp(m, 0);
        for (auto c : s) {
            for (int i = m-1; i >= 0; i--) {
                if (c == t[i]) {
                    dp[i] = (i > 0? dp[i-1]: 1) + dp[i];
                }
            }
        }
        return (int)dp[m-1];
    }
};
leetcode115(解法2)

题意:给定字符串 s 和 t,求 s 中有多少个子串和 t 相等

解法1:设置 dp[i][j] 表示 s[0, i) 部分的子串和 t[0, j) 相等的个数。当 s[i] == t[j] 时,意味着 s[i] 这个位置的字符可留可不留,此时 dp[i][j] = dp[i-1][j] + dp[i-1][j-1]。

当 s[i] != t[j] 时,意味着 s[i] 这个位置一定不留,此时 dp[i][j] = dp[i-1][j]。初始化时 dp[i][0] = 1,即空串是所有串的子串

解法2:详细解答见:https://leetcode.com/problems/distinct-subsequences/discuss/37388/4ms-7-lines-c%2B%2B-dp-solution!-very-clear-almost-best!

 

14.Leetcode1035

class Solution {
public:
    int maxUncrossedLines(vector<int>& A, vector<int>& B) {
        int n = A.size(), m = B.size();
        vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (A[i] == B[j]) {
                    dp[i+1][j+1] = dp[i][j] + 1;
                } else {
                    dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1]);
                }
            }
        }
        return dp[n][m];
    }
};
leetcode1035

最长公共子串

 

15.Leetcode1312

class Solution {
public:
    int minInsertions(string s) {
        int n = s.size();
        vector<vector<int>> dp(n, vector<int>(n, 0));
        for (int len = 2; len <= n; len++) {
            for (int i = 0, j = i+len-1; j < n; i++, j++) {
                if (s[i] == s[j]) {
                    dp[i][j] = dp[i+1][j-1]; 
                } else {
                    dp[i][j] = min(dp[i+1][j]+1, dp[i][j-1]+1);
                }
            }
        }
        return dp[0][n-1];
    }
};
leetcode1312

设置 dp[i][j] 表示使得 s[i, j] 满足回文串条件最少需要插入的数量。if (s[i] == s[j]) dp[i][j] = dp[i+1][j-1]; else dp[i][j] = min(dp[i+1][j]+1, dp[i][j-1]+1)

 

16.Leetcode712

class Solution {
public:
    int minimumDeleteSum(string s1, string s2) {
        int n = s1.size(), m = s2.size();
        vector<vector<int>> dp(n+1, vector<int>(m+1));
        for (int i = 1; i <= n; i++) {
            dp[i][0] = dp[i-1][0] + s1[i-1];
        }
        for (int i = 1; i <= m; i++) {
            dp[0][i] = dp[0][i-1] + s2[i-1];
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (s1[i] == s2[j]) dp[i+1][j+1] = dp[i][j];
                else {
                    int val1 = s1[i], val2 = s2[j];
                    dp[i+1][j+1] = min(dp[i+1][j]+val2, dp[i][j+1]+val1);
                }
            }
        }
        return dp[n][m];
    }
};
leetcode712

删除两个字符串中的一些字符,使得两个字符串一致。求最小删除字符的 ASCII 之和。最长公共子串变形

 

17.Leetcode1278

class Solution {
public:
    int palindromePartition(string s, int K) {
        int n = s.size();
        vector<vector<int>> pd(n, vector<int>(n)), dp(n, vector<int>(K+1));
        for (int len = 2; len <= n; len++) {
            for (int i = 0; i < n; i++) {
                int j = i+len-1;
                if (j >= n) break;
                pd[i][j] = pd[i+1][j-1];
                if (s[i] != s[j]) pd[i][j]++;
            }
        }
        for (int i = 0; i < n; i++) dp[i][1] = pd[0][i];
        for (int k = 2; k <= K; k++) {
            for (int i = k-1; i < n; i++) {
                dp[i][k] = i+1;
                for (int j = i-1; j >= k-2; j--) {
                    dp[i][k] = min(dp[i][k], dp[j][k-1]+pd[j+1][i]);
                }
            }
        }
        return dp[n-1][K];
    }
};
leetcode1278

pd[i][j] 表示将字符串 s[i: j] 变成回文串最少需要改变的字符数。dp[i][k] 表示将字符串前 i 位分成 k 份满足条件的子串最少需要改变的字符。

if (s[i] == s[j]) pd[i][j] = pd[i+1][j-1]; else pd[i][j] = pd[i+1][j-1]+1;

dp[i][k] = min(dp[i][k], dp[j][k-1]+pd[j+1][i])

 

18.Leetcode813

class Solution {
public:
    double largestSumOfAverages(vector<int>& A, int K) {
        int n = A.size();
        vector<int> sum(n);
        sum[0] = A[0];
        for (int i = 1; i < n; i++) sum[i] = sum[i-1]+A[i];
        vector<vector<double>> avg(n, vector<double>(n));
        for (int i = 0; i < n; i++) avg[i][i] = sum[i];
        for (int i = 1; i < n; i++) avg[0][i] = 1.0*sum[i]/(i+1);
        for (int i = 1; i < n; i++) {
            for (int j = i-1; j >= 0; j--) {
                avg[j+1][i] = 1.0*(sum[i]-sum[j])/(i-j);
            }
        }
        vector<vector<double>> dp(n, vector<double>(K+1));
        for (int i = 0; i < n; i++) dp[i][1] = avg[0][i];
        for (int k = 2; k <= K; k++) {
            for (int i = k-1; i < n; i++) {
                for (int j = 0; j < i; j++) {
                    dp[i][k] = max(dp[i][k], dp[j][k-1]+avg[j+1][i]);
                }
            }
        }
        return dp[n-1][K];
    }
};
leetcode813

类似于上一题。先求出 avg[i][j] 表示数组 [i, j] 范围内的平均值。接着利用和上题一样的思路即可求出

 

19.Leetcode1335

class Solution {
public:
    int minDifficulty(vector<int>& A, int d) {
        int n = A.size();
        if (n < d) return -1;
        vector<vector<int>> pd(n, vector<int>(n));
        for (int i = 0; i < n; i++) pd[i][i] = A[i];
        for (int len = 2; len <= n; len++) {
            for (int i = 0; i < n; i++) {
                int j = i+len-1;
                if (j >= n) break;
                pd[i][j] = max(pd[i+1][j], pd[i][j-1]);
            }
        }
        vector<vector<int>> dp(n, vector<int>(d+1));
        for (int i = 0; i < n; i++) dp[i][1] = pd[0][i];
        for (int k = 2; k <= d; k++) {
            for (int i = k-1; i < n; i++) {
                dp[i][k] = INT_MAX;
                for (int j = k-2; j < i; j++) {
                    dp[i][k] = min(dp[i][k], dp[j][k-1]+pd[j+1][i]);
                }
            }
        }
        return dp[n-1][d];
    }
};
leetcode1335

 类似于上一题。先求出 pd[i][j] 表示数组 [i, j] 范围内的最大值。接着利用和上题一样的思路即可求出

 

20.Leetcode516

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int n = s.size();
        vector<vector<int>> dp(n, vector<int>(n, 0));
        for (int i = 0; i < n; i++) dp[i][i] = 1;
        for (int len = 2; len <= n; len++) {
            for (int i = 0; i < n; i++) {
                int j = i+len-1;
                if (j >= n) break;
                if (s[i] == s[j]) dp[i][j] = dp[i+1][j-1]+2;
                else dp[i][j] = max(dp[i+1][j], dp[i][j-1]);
            }
        }
        return dp[0][n-1];
    }
};
leetcode516

区间 DP. if (s[i] == s[j]) dp[i][j] = dp[i+1][j-1]+2; else dp[i][j] =  max(dp[i+1][j], dp[i][j-1]);

 

21.Leetcode312

class Solution {
public:
    int maxCoins(vector<int>& nums) {
        int n = nums.size();
        vector<int> newNums(n+2, 1);
        for (int i = 1; i <= n; i++) {
            newNums[i] = nums[i-1];
        }
        vector<vector<int>> dp(n+2, vector<int>(n+2));
        for (int i = 1; i <= n; i++) {
            dp[i][i] = newNums[i-1] * newNums[i] * newNums[i+1];
        }
        for (int len = 2; len <= n; len++) {
            for (int i = 1; i <= n; i++) {
                int j = i+len-1;
                if (j > n) break;
                dp[i][j] = max(newNums[i-1]*newNums[i]*newNums[j+1]+dp[i+1][j], dp[i][j-1]+newNums[i-1]*newNums[j]*newNums[j+1]);
                for (int k = i+1; k < j; k++) {
                    dp[i][j] = max(dp[i][j], dp[i][k-1]+newNums[i-1]*newNums[k]*newNums[j+1]+dp[k+1][j]);
                }
            }
        }
        return dp[1][n];
    }
};
leetcode312

题意:给定一组整数,按任意的顺序删掉一个数直到删掉了所有的数。删掉一个数所得到的分数是其当前左右两边的数和自己的成绩。求最后最大的分数是多少

设置 dp[i][j] 表示在数组 [i, j] 的范围内,满足条件最大的分数是多少。当确定了 i 和 j 的值后,需要枚举 k (i <= k <= j) 表示在区间 [i, j] 范围内最后一个删掉的数是什么

 

22.Leetcode375

class Solution {
public:
    int getMoneyAmount(int n) {
        vector<vector<int>> dp(n+1, vector<int>(n+1));
        for (int len = 2; len <= n; len++) {
            for (int i = 1; i+len-1 <= n; i++) {
                int j = i+len-1;
                dp[i][j] = min(i+dp[i+1][j], j+dp[i][j-1]);
                for (int k = i+1; k < j; k++) {
                    dp[i][j] = min(dp[i][j], k+max(dp[i][k-1], dp[k+1][j]));
                }
            }
        }
        return dp[1][n];
    }
};
leetcode375

和上一题类似。关键点在于 k 代表第一次猜哪个位置的值,dp[i][j] = min(dp[i][j], k + max(dp[i][k-1], dp[k+1][j]));

 

23.Leetcode1000

class Solution {
public:
    int mergeStones(vector<int>& stones, int K) {
        int n = stones.size();
        if ((n - 1) % (K - 1) != 0) return -1;
        vector<int> sums(n + 1);
        vector<vector<int>> dp(n, vector<int>(n));
        for (int i = 1; i < n + 1; ++i) {
            sums[i] = sums[i - 1] + stones[i - 1];
        }
        for (int len = K; len <= n; ++len) {
            for (int i = 0; i + len <= n; ++i) {
                int j = i + len - 1;
                dp[i][j] = INT_MAX;
                for (int t = i; t < j; t += K - 1) {
                    dp[i][j] = min(dp[i][j], dp[i][t] + dp[t + 1][j]);
                }
                if ((j - i) % (K - 1) == 0) {
                    dp[i][j] += sums[j + 1] - sums[i];
                }
            }
        }
        return dp[0][n - 1];
    }
};
leetcode1000

详细解释见:https://leetcode.com/problems/minimum-cost-to-merge-stones/discuss/247567/JavaC%2B%2BPython-DP

posted @ 2020-06-20 11:33  HDU_jackyan  阅读(227)  评论(0编辑  收藏  举报