题目
给定一个数组arr,代表一排有分数的气球。没打爆一个气球都能获得分数,假设打爆气球的分数为X,获得分数的规则如下:
- 如果被打爆气球的左边有没有被打爆的气球,找到离被打爆气球最近的气球,假设分数为L;如果被打爆气球的右边有没被打爆的气球,找到离被打爆气球最近的气球,假设位数为R,那么获得分数为LRX。
- 如果被打爆气球的左边有没有被打爆的气球,找到离被打爆气球最近的气球,假设分数为L;右边没有没被打爆的气球,那么获得分为为L*R。
- 如果被打爆气球左边没有没被打爆的气球,如果被打爆气球的右边有没被打爆的气球,找到离被打爆气球最近的气球,假设位数为R,那么获得分数为R*X。
目标是打爆所有的气球,获得每次被打爆的分数。通过选择不同的顺序,可以得到不同的总分,求能获得的最大分数。
分析
这也是一道能通过爆搜递归然后进行动态规划优化的模板题。复习 换钱的方法数
下面分别总结爆搜和动态规划
解法1 暴力递归
可以分为以下几种情况:
假设要打爆arr[L…R]范围的气球,并假设arr[L-1]和arr[R+1]的气球还没被打爆,那么获取最大的分的函数为process(L,R)
-
如果arr[L]最后被打爆,那么递归为
process(L+1,R) + arr[L-1]*arr[L]*arr[R+1]
-
如果arr[R]最后被打爆,那么递归为
process(L,R-1) + arr[L-1]*arr[R]*arr[R+1]
-
如果arr[i]最后被打爆,那么递归为
process(L,i-1) + process(i+1,R) + arr[L-1]*arr[i]*arr[R+1]
有了递归方程,那么递归很容易写出
// 暴力递归
int process(int arr[], int L ,int R){
if(L==R){
return arr[L-1]*arr[L]*arr[R+1];
}
int maxValue = max(
arr[L-1]*arr[L]*arr[R+1]+process(arr, L+1,R),
arr[L-1]*arr[R]*arr[R+1]*process(arr, L,R-1)
);
for(int i=L+1;i<=R-1;i++){
maxValue = max(
maxValue,
arr[L-1]*arr[i]*arr[R+1]+process(arr, L , i-1)+process(arr,i+1, R)
);
}
return maxValue;
}
在写正式函数的过程,arr可以用一个辅助数组,arr[0]=1,arr[N+1]=1就可以不用考虑边界问题
解法2 动态规划优化
按照暴力递归优化为动态规划的套路:
- 首先判断无后效性,可变参数L和R确定了以后,那么返回值就确定了(无后效性)
- 然后将L和R作为横纵坐标组成一张表,这种表包括了所有的返回值。
// 动态规划题解
int processDP(int arr[], int length){
int N = length;
int help[length+2];
memset(help,sizeof(help),1);
for(int i=0;i<N;i++){
help[i+1] = arr[i];
}
int dp[length+1][length+1];
memset(dp, sizeof(dp),0);
for(int i=1;i<=N;i++){
dp[i][i] = help[i]*help[i];
}
for(int L=N;L>=1;L++){
for(int R=L+1;R<=N;R--){
int finalL = help[L-1]*help[R]*help[L]+ dp[L+1][R];
int finalR = help[L-1]*help[R+1]*help[R]+ dp[L][R-1];
dp[L][R] = max(finalL,finalR);
for(int i=L+1;i<=R;i++){
dp[L][R] = max(dp[L][R],dp[L][i-1]+dp[i+1][R]+help[L-1]*help[i]*help[R+1]);
}
}
}
return dp[1][N];
}