312. Burst Balloons

Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. Here left and right are adjacent indices of i. After the burst, the left and right then becomes adjacent.

Find the maximum coins you can collect by bursting the balloons wisely.

Note:
(1) You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them.
(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100

Example:

Given [3, 1, 5, 8]

Return 167

1
2
3
>     nums = [3,1,5,8] --> [3,5,8] -->   [3,8]   -->  [8]  --> []
> coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167
>

​ 题意:戳破一个气球(序号为i)就可以得到nums[i-1]nums[i]\nums[i+1]的得分,求问怎么得戳破所有气球然后得分最高。

​ 一开始看这个题真的是一点思路都没有,别说敲代码实现了,就是自己手动算的都不知道从何而算起。

​ 没有思路的主要问题是没办法把这个题目划分为子问题,好像觉得在每一步要戳破哪个很难决定。最后看了discuss后恍然大悟,要这么划分自问题的:

​ 要戳破[i,j]区间的气球,那么如果k为最后一个戳破的,那么以d[i][j]表示得分,则

1
2
3
dp[i][j] = {
dp[i][k-1]/*戳破k左边的得分*/+dp[k+1][j]/*戳破k右边的得分*/+nums[i-1]*nums[k]*nums[j+1]/*戳破k的的得分*/
}

​ 这样子问题不就划分好了吗~?

​ 需要计算最大值,那么只要按个遍历[i,j]中的每一个可能值k,然后维护最大值即可。表示如下:

1
2
3
for k in [i,j]:
dp[i][j] = max(dp[i][j],
dp[i][k - 1] + dp[k + 1][j] + balloons[i - 1] * balloons[k] * balloons[j + 1]);

​ 虽然到此思路有了,但是一开始提交还是错掉了。主要是对dp二维表的计算顺序搞错了!

错误的计算顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//
// Created by chaopengz on 2017/11/21.
//
#include "head.h"

class Solution {
public:
int maxCoins(vector<int> &nums)
{
int len = nums.size();
vector<int> balloons;
balloons.push_back(1);
for (auto x:nums)
balloons.push_back(x);
balloons.push_back(1);

vector<vector<int>> dp(balloons.size(), vector<int>(balloons.size(), 0));

for (int i = 1; i <= len; ++i)
{
for (int j = i; j <= len; ++j)
{//计算dp[i][j]
for (int k = i; k <= j; ++k)//k为[i,j]最后一个戳破的气球
{
dp[i][j] = max(
dp[i][j],
dp[i][k - 1] + dp[k + 1][j] + balloons[i - 1] * balloons[k] * balloons[j + 1]
);
}
}
}

return dp[1][len];
}
};

​ 错误主要是因为:跟之前的第i行的结果依赖第i-1行的思想不一样,这次的是i+1行对i行也有影响,所以对角线的要先算,比如在计算dp[1][2]时,当k=1时

1
dp[1][2] = max(dp[1][2],dp[1][0]+dp[2][2]+balloons[0]*ballons[1]*ballons[3])//这时要dp[1][2]就得先有dp[2][2]

正确的AC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//
// Created by chaopengz on 2017/11/21.
//
#include "head.h"

class Solution {
public:
int maxCoins(vector<int> &nums) {
int len = nums.size();
vector<int> balloons;
balloons.push_back(1);
for (auto x:nums)
balloons.push_back(x);
balloons.push_back(1);

vector<vector<int>> dp(balloons.size(), vector<int>(balloons.size(), 0));

for (int step = 0; step < len; ++step) {
for (int i = 0; i <= len - step; ++i) {
int j = i + step;
for (int k = i; k <= j; ++k)//k为[i,j]最后一个戳破的气球
{
dp[i][j] = max(
dp[i][j],
dp[i][k - 1] + dp[k + 1][j] + balloons[i - 1] * balloons[k] * balloons[j + 1]
);
}
}
}
return dp[1][len];
}
};