算法设计与分析第九次作业

leetcode 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:
You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them.
0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100
Example:
Input: [3,1,5,8]
Output: 167
Explanation: nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> []
coins = 315 + 358 + 138 + 181 = 167

这道题要求我们以一定的顺序来扎破气球,而每个气球爆炸后可以获得一定的硬币数,硬币数即为当前气球和左右相邻硬币的数的乘积,如果气球爆炸则左右气球变为相互相邻的,求最大的硬币数,这道题如果用暴力搜索的话复杂度是O(n!),而且仔细思考了一下似乎没有什么很特别的规律,所以应该还是要用搜索所有可能情况然后比较大小的方法来做,所以很自然的就会想到要用动态规划,要保存数据的方式应该也不难想,一维数组肯定是不够的,因为扎破气球的顺序可以是随机的,至少也需要取一个小的子序列进行动态规划,也就是用result[i][j]来存储i和j这两个元素之间的气球全部爆炸可以获得的最大收益,因为但是这道题的难点就在于在扎破一个气球之后是会影响气球的相邻关系的,所以情况就会变得很复杂,比如我已经确认了1到k-1个元素的最大硬币值以及k+1到n个元素的最大硬币值,但是此时k的邻居是完全未知的,因为我并不知道这两个序列扎破气球的顺序是怎么样的,而且退一步讲其实就算知道了,因为k这里还有一步是要加上扎破k时的硬币数,这样在两个序列取最大的时候其实未必是全局的最大值。

而其实解决这个问题的关键就是给定k被扎破的时间,我们可以规定result[i][j]是不包括两个端点的,也就是这两个端点的气球不被扎破,然后规定k是序列中最后被扎破的,这样问题就简单了很多,因为既然k是最后被扎破的,那么它在被扎破时的邻居就变成确定的了,也就是两个端点,因为其他的气球都已经被扎破了,同样我们采用记忆化搜索的过程来实现动态规划过程,以免计算不需要的值,具体代码实现如下:

class Solution {
public:
    int maxCoins(vector<int>& nums) {
    	//首尾都加上1,根据题干描述首尾的硬币值是1但是不能被扎破
        arr.push_back(1);
        for(int i : nums){
        	//去掉值为0的元素不会对最后的结果产生任何的影响,因为肯定是要先把0先扎破的,不然邻居是0的话乘积自然会小
        	if(i > 0){
        		arr.push_back(i);
        	}
        }
        arr.push_back(1);
        int n = arr.size();
        //result[i][j]存放扎破i,j之间的元素获得硬币最大值,这里为了编程方便就用一维数组代替了
        result = new int[n*n];
        for(int i = 0; i < n*n; i++){
        	result[i] = -1;
        }
        //因为0和n-1此时存放的都是我们加进去的1,所以不需要被扎破,刚好符合我们的要求
        int ans = dp(0,n-1,n);
        delete[] result;
        return ans;
    }
private:
	int dp(int start, int end, int arrSize){
		//中间没有元素自然值为空
		if(end - start <= 1){
			return 0;
		}
		//之前已经计算过这个值就直接返回
		else if(result[start*arrSize + end] != -1){
			return result[start*arrSize + end];
		}
		int maxNum = 0;
		//分别对中间的每个元素是最后扎破的进行假设遍历,最后取总体值最大的那个
		for(int i = start + 1; i < end; i++){
			maxNum = max(maxNum, arr[start]*arr[i]*arr[end] + dp(start,i,arrSize) + dp(i,end,arrSize));
		}
		result[start*arrSize + end] = maxNum;
		return maxNum;
	}
	vector<int> arr;
	int* result;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值