题目
有 n 个气球,编号为0 到 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums 中。
现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。
这里的 i - 1 和 i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1或 i + 1
超出了数组的边界,那么就当它是一个数字为 1 的气球。求所能获得硬币的最大数量。
思路
区间dp:
 定义 f[i][j]为考虑将 (i,j)范围内(不包含 i 和 j 边界)的气球消耗掉,所能取得的最大价值。
考虑在开区间内最后一个被戳破的气球为k,即k是这个区间内最后一个被戳破的气球,换句话说,目前剩下的气球就是只剩下开区间的首尾i和j,以及最后一个被戳破的气球k了
假设 dp[i][j] 表示开区间 (i,j) 内能拿到的最多金币   那么这个情况下 ,在 (i,j) 开区间得到的金币可以由 dp[i][k] 和 dp[k][j] 进行转移,如果此时选择戳爆k气球,那么得到的金币就是total = dp[i][k] + val[i] * val[k] * val[j] + dp[k][j],其中val[i] 表示 i 位置气球的数字
也正是因为 k 是最后一个被戳爆的,所以 (i,j) 区间中 k 两边的东西必然是先各自被戳爆了的, 左右两边互不干扰
最后在 (i,j) 区间中选取的k可以是多个,进行一个枚举,从中选择使得 total 值最大的即可用来更新 dp[i][j],然后从 (i,j) 开区间只有三个数字的时候开始计算,储存每个小区间可以得到金币的最大值 然后慢慢扩展到更大的区间,即j不断扩大,利用小区间里已经算好的数字来算更大的区间即可
java代码如下:
class Solution{
	public int maxCoins(int[] nums){
		int n = nums.length;
		//创建一个辅助数组,在首尾各添加1,方便处理边界问题
		int[] temp = new int[n + 2];
		temp[0] = 1;
		temp[n+1] = 1;
		//填充原数组的值
		for(int i = 0; i < n; i++){
			temp[i + 1] = nums[i];
		} 
		int[][] dp = new int[n + 2][n + 2];
		//len表示开区间长度,从(i,k,j)开始循环,逐渐扩大
		for(int len = 3; len <= n + 2; len++){
			for(int i = 0; i <= n + 2 - len; i++){
				int res = 0;
				//k为开区间(i,j)内的索引
				for(int k = i + 1; k < i + len - 1; k++){//范围不断扩大
					int left = dp[i][k];//表示区间(i,k)
					int right = dp[k][i + len -1];//表示区间(k,i + len - 1)
					res = Math.max(res,left + temp[i] * temp[k] * temp[i + len - 1] + right);
				}
				dp[i][i + len - 1] = res;//不断记录开区间的最大金币
			}
		}
		return dp[0][n+1];
	}
}




















