问题:有N个气球A[1],...A[N],扎破第i个气球所得的金币A[left]*A[i]*A[right],扎破气球i后,
气球left和right就变成相邻的气球,求获得最多的金币数,A[0] = A[N+1] = 1
例:
A = [3,1,5,8]
[3,5,8]-->[3,8]-->[8]-->[]
金币数3*1*5+3*5*8+1*3*8+1*8*1 = 167
问题分析:
从最后一步开始,对于A[1],..,A[N]来说,假设最后一个炸的是A[i]
扎破A[i]时,左边气球是A[0],右边是A[N+1],此时获得金币是1*A[i]*1
气球A[1,...,i-1]和气球A[i+1,...,N]均已被扎破
区间从A[1,...,N],现在变成A[1,...,i-1]和A[i+1,...,N],也就是说区间变小
是一个区间型dp
状态转移:
设:f[i][j]表示扎破气球A[i+1,...,j-1]时最多获得的金币数,A[i]和A[j]不能扎破
f[i][j] = max{f[i][k]+f[k][j]+A[i]*A[k]*A[j]}(i < k < j)
枚举区间A[i,...,j]最后一个扎破的气球A[k];最后是A[i],A[k],A[j],此时金币数是A[i]*A[k]*A[j]
f[i][j] = max{区间A[i,...,K] + 区间A[k,...,j] + 最后A[i]*A[k]*A[j]}
初始条件:
当区间A[i,..,j]中间没有球时,即i+1 = j,得到金币0
f[0][1],f[1][2],...,f[N][N+1] = 0
计算顺序:按j-i区间从小到大计算
区间长度2: f[0][1],f[1][2],...,f[N][N+1]
区间长度3: f[0][2],f[1][3],...,f[N-1][N+1]
.
.
区间长度N+2: f[0][N+1]
时间复杂度O(N^3),空间复杂度O(N^2)
代码及注释如下:
def burst_balloons(B):
n = len(B)
if n == 0:
return 0
#保证A[0] = A[N+1] = 1,A[1,...,N] =B
A = [1 for i in range(n+2)]
for i in range(1,n+1):
A[i] = B[i-1]
#创建并初始化f,f[i][i+1] = 0
f = [[0 for i in range(n+2)] for j in range(n+2)]
#区间长度3,...,n+2
for l in range(3,n+3):
#i是区间起始点,因为区间长度为l,所以当区间结束点j = n+2时,i = n+2-l = n-l+2,也就是i能到达最右的点
for i in range(0,n-l+3):
j = i+l-1
#枚举区间i,,,j最后一个扎破球的位置k
for k in range(i+1,j):
#f[i][j] = max{f[i][k]+f[k][j]+A[i]*A[k]*A[j]}(i < k < j)
f[i][j] = max(f[i][j],f[i][k]+f[k][j]+A[i]*A[k]*A[j])
return f[0][n+1]
B = [3,1,5,8]
print(burst_balloons(B))
#结果:167