312. 戳气球

Q:

有 n 个气球,编号为0 到 n-1,每个气球上都标有一个数字,这些数字存在数组 nums 中。

现在要求你戳破所有的气球。每当你戳破一个气球 i 时,你可以获得 nums[left] * nums[i] * nums[right] 个硬币。 这里的 left 和 right 代表和 i 相邻的两个气球的序号。注意当你戳破了气球 i 后,气球 left 和气球 right 就变成了相邻的气球。

求所能获得硬币的最大数量。

说明:

你可以假设 nums[-1] = nums[n] = 1,但注意它们不是真实存在的所以并不能被戳破。
0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100
示例:

输入: [3,1,5,8]
输出: 167
解释: nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> []
coins = 315 + 358 + 138 + 181 = 167

A:

先上代码,下面解释

class Solution {
public:
	int maxCoins(vector<int>& nums) 
	{
		int res = 0;
		int n = nums.size();
		nums.insert(nums.begin(), 1);
		nums.push_back(1);	//气球编号1-n,存储在索引1-n
		vector<vector<int>> dp(n+2, vector<int>(n+2, 0));
		//dp[i][j]表示i到j所有气球爆掉得到的最多金币数
		for (int len = 1; len <= n; ++len)
		{
			for (int start = 1; start + len - 1 <=n; ++start)
			{
				int cur_max = 0, end = start + len - 1;	//[start,end],长度len
				for (int boom = start; boom <= end; ++boom)
				{
					cur_max = max(cur_max, dp[start][boom - 1] + dp[boom + 1][end] + nums[start - 1] * nums[boom] * nums[end + 1]);
				}
				dp[start][end] = cur_max;
			}
		}
		// for (auto row : dp)
		// {
		// 	for (auto col : row)
		// 	{
		// 		printf("%3d", col);
		// 	}
		// 	cout << endl;
		// }
		return dp[1][n];
	}	
};

我觉得关键就是dp方程的理解。求i到j父结构的最优解时,我们在中间任意位置k开刀,将k爆掉再求左右子结构的最优解。但这里有个问题,爆了k之后,左边的子结构为i到k-1,右边子结构为k+1到j。拿左边举例,这个子结构的解我们已经求好了(因为我们用的是自底向上的dp),**而这个求好的解是建立在没有爆k的情况下的!**所以我们如果要利用子结构解的话,前提就变成了左右子结构i到k-1和k+1到j都已经爆掉,**即此时k的左右气球应该分别是i-1和j+1。**这就是我对dp方程中加的为什么不是相邻三个气球的值相乘值的解释。

posted @ 2019-10-21 17:34  NeoZy  阅读(125)  评论(0)    收藏  举报