312. 戳气球 每日一题 Golang 动态规划

312. 戳气球 每日一题 Golang 动态规划

312. 戳气球 每日一题 Golang 动态规划_第1张图片

最容易的想到的是递归求解,但是必然会造成大量的重复计算。那能不能利用动态规划拿空间换时间呢?
由于扎破一个气球后数组就发生了变化,不满足动态规划的无后效性
到这里我陷入了困境。
看了题解的思路,真的太巧妙了,技巧性太强了。

题解的关键在于 倒过来考虑逐个戳破的气球。
我们在两边各加一个数值为1的气球。
我们构造dp矩阵,行列分别表示气球序列的起点和终点,dp[i][j]表示戳破(i,j)(注意为开区间)中的气球的最多得分。

1 3 1 5 8 1
1
3
1
5
8
1

结合示例用例来看,我们最终要求的是dp[0][5]
对于dp[i][j],(i,j)至少要包含一个气球,因此j>i+1。

1 3 1 5 8 1
1 0 0
3 0 0 0
1 0 0 0 0
5 0 0 0 0 0
8 0 0 0 0 0 0
1 0 0 0 0 0 0

假如最后一个戳破的气球的数值为num[i]
我们来考虑dp[0][5],假设最后一个戳破气球5,那么可以想到最后的局面是只有5被左右边界的1夹住,得分 1(左边界) × 5(当前值) × 1(右边界)=5
再考虑之前的得分,5(index=3)左边戳破的气球的最大得分是dp[0][3],右边戳破的气球的最大得分是dp[3][5],相加就是这种情况下的得分。
注意这里的状态转移,需要递归求解的是子串,结构并没有变化。
通过这种逆向的奇妙思路构造了满足无后效性的状态转移。
根据以上方法,分别假设最后一次戳破3,1,5,8,取最大的得分。
如果以上仍然不能帮助你理解,还是建议去看官方题解。

我们发现求一个dp[i][j]需要其左侧和下侧的值,因此我们这样扫描。
312. 戳气球 每日一题 Golang 动态规划_第2张图片
或者这样扫描。
312. 戳气球 每日一题 Golang 动态规划_第3张图片
动态规划

func max(a, b int) int {
	if a < b {
		return b
	} else {
		return a
	}
}

func buildMatrix(m,n int) (ans [][]int){
	ans=make([][]int,m)
	for i:=0;i<m;i++{
		ans[i]=make([]int,n)
	}
	return
}

func maxCoins(nums []int) int {
	nums = append([]int{1}, nums...)
	nums = append(nums, 1)
	dp:=buildMatrix(len(nums), len(nums))
	for i:= len(nums)-3;i>= 0;i--{
		for j:=i+2;j< len(nums);j++{
			for k:=i+1;k<j;k++{
				dp[i][j]=max(dp[i][j],nums[k]*nums[i]*nums[j]+dp[i][k]+dp[k][j])
			}
		}
	}
	return dp[0][len(nums)-1]
}

你可能感兴趣的:(算法,算法,动态规划,leetcode,go)