LeetCode/solutions/312. Burst Balloons.md
2019-11-09 18:04:59 +08:00

2.4 KiB
Raw Blame History

312. Burst Balloons

思路、区间动归

题目介绍了一种打气球的游戏每个气球上面都有一个数字每次打爆一个气球得到的金币数是被打爆的气球及其两边的气球上的三个数字之积如果旁边没有气球了则按1算。求将所有气球打爆能得到的最多金币总数。

如果我们将首尾各插入一个数字为1的气球那么首尾这两个气球是不能打的这样这题就和Poj 1651:Multiplication Puzzle基本一样了。那么在这种情况下该怎么打气球呢?

首尾各插入一个数字为1的气球后有以下几种情况

  1. 如果不到三个气球那么得到的金币总数显然为0
  2. 如果刚好三个气球,那么得到的金币总数也可以直接算出来就是中间气球上的数字;
  3. 如果大于三个气球假设我们将第ki < k < j个气球作为分界线那么先按照最优策略打爆k之前的剩下第一个气球再按照最优策略打爆k之后的剩下最后一个气球那么此时还剩下三个气球首尾加上第k个最后将第k个气球打爆结束。

如果dp[i][j]表示第i个气球到第j个气球的最优打法所获得的金币总数那么上面第3中情况即代表了转移方程

for all k in [i+1, ..., j-1]:
  dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + nums[i]*nums[k]*nums[j]);

需要注意的是上述的dp[i][k]dp[k][j]都代表的是最优策略,而因为i < k所以在最外层循环i是从最大值往0递减循环 而不是从0开始递增循环

C++

class Solution {
public:
    int maxCoins(vector<int>& nums) {
        nums.insert(nums.begin(), 1);
        nums.push_back(1);
        
        int N = nums.size();
        vector<vector<int>>dp(N, vector<int>(N, 0));
        // 注意这里i是从最大值递减循环!
        for(int i = N - 1; i >= 0; i--){
            for(int j = i + 1; j < N; j++){
                // if(j == i + 1) dp[i][j] = 0;
                // else if(j == i + 2) dp[i][j] = nums[i] * nums[i+1] * nums[i+2];
                // else{
                    for(int k = i+1; k < j; k++)
                        dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + nums[i]*nums[k]*nums[j]);
                // }
            }
        }

        return dp[0][N-1];
    }
};