• 专题训练之 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

  • 相关阅读:
    centos8 将SSSD配置为使用LDAP并要求TLS身份验证
    Centos8 搭建 kafka2.8 .net5 简单使用kafka
    .net core 3.1 ActionFilter 拦截器 偶然 OnActionExecuting 中HttpContext.Session.Id 为空字符串 的问题
    Springboot根据不同环境加载对应的配置
    VMware Workstation12 安装 Centos8.3
    .net core json配置文件小结
    springboot mybatisplus createtime和updatetime自动填充
    .net core autofac依赖注入简洁版
    .Net Core 使用 redis 存储 session
    .Net Core 接入 RocketMQ
  • 原文地址:https://www.cnblogs.com/HDUjackyan/p/13029486.html
Copyright © 2020-2023  润新知