题目来源
题目描述
题目解析
只要涉及到求最值,就只能穷举所有的可能,然后对比得出最值。
问题是,怎么穷举出所有的结果呢?
穷举主要有两种算法:
- 回溯:本质是暴力穷举
- 动态规划:本质根据状态转移方程推导[状态]
回溯
怎么回溯呢?我们其实就是想穷举戳气球的顺序,不同的顺序可能得到不同的分数,我们需要把所有可能的分数中最高的那个找出来。
可以看出,这其实是一个全排列问题。伪代码思路如下:
int res = MIN_VALUE;
// 输入一组气球,返回戳破它们获得的最大分数
int maxCoins(int [] nums){
backtrack(nums, 0);
return res;
}
// 回溯伪代码
void backtrack(int [] nums, int score){
if(nums为空){
res = std::max(res, score);
return;
}
for(int i = 0; i < nums.length; i++){
int point = nums[i - 1] * nums[i] * nums[i + 1];
int temp = nums[i];
// 选择:
在num中删除nums[i];
// 递归回溯
backtrack(nums, score + point);
// 撤销选择
将 temp 还原到 nums[i]
}
}
但是时间复杂度是阶乘级别
动态规划
这道题和一般的动态规划的不同之处在于:我们每戳破一个气球nums[i],得到的分数和该气球相邻的nums[i-1]和nums[i+1]
是相关的
而动态规划算法要求子问题必须独立,所以我们想要使用动态规划,必须巧妙定义dp数组的含义,避免子问题的相关性,才能退出合理的状态转移方程
思路一
(1)确定状态
- 所有N个气球都被扎破
- 最后一步:
- 一定有最后一个被砸破的气球,变化是i
- 砸破时,左边是气球0,右边是气球N+1,获得金币 1 ∗ a i ∗ 1 = a i 1*a_i*1 = a_i 1∗ai∗1=ai
- 此时气球 1 i − 1 1~i-1 1 i−1以及 i + 1 N i+1~N i+1 N都已经被扎破 ,并且已经获得对应金币
- 子问题:
- 要求扎破1~N号气球,最多获得的金币数
- 需要知道砸破 1 i − 1 1~i-1 1 i−1号气球,最多获得的金币数,以及砸破 i + 1 N i+1~N i+1 N号气球,最多获得的金币数
- 状态:设 f [ i ] [ j ] f[i][j] f[i][j]为扎破 i + 1 j − 1 i+1~j-1 i+1 j−1号气球,最多获得的金币数(j和j不能被扎破)
(2)转移方程
(3)初始条件
- 初始条件:
- f [ 0 ] [ 1 ] = f [ 1 ] [ 2 ] = . . . f [ N ] [ N + 1 ] = 0 f[0][1] = f[1][2]=...f[N][N+1] = 0 f[0][1]=f[1][2]=...f[N][N+1]=0,当没有气球要扎破时,最多获得0枚金币
(4)计算顺序
- 区间型状态规划:按照长度j-i从小到大的顺序去算
- f[0][1]、f[1][2]…f[N][N+1]
- f[0][2]、f[1][3]…f[N-1][N+1]
- …
- f[0][N+1]