- Leetcode 312. 戳气球(记忆化搜索)
- 题目
- 有 n 个气球,编号为0 到 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums 中。
- 现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。这里的 i - 1 和 i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1或 i + 1 超出了数组的边界,那么就当它是一个数字为 1 的气球。
- 求所能获得硬币的最大数量。
- n == nums.length
- 1 <= n <= 300
- 0 <= nums[i] <= 100
- 解法
- 动态规划:设 val[i+1]=nums[i] 同时 val[0]=val[n+1]=1 头尾加 1 方便计算,然后定义 dp[i][j] 为戳破(i,j)开区间所有气球获得最多金币数量,n 个气球所能获得的最大金币就是 dp[0][n+1] 的值,接下来求转移方程式
- 转移方程式:我们假设最后戳破的是第 k 个气球、同时 i<k<j,可得转移方程式:dp[i][j] = Max(dp[i][k] + val[i]*val[k]*val[j] + dp[k][j]),代表(i,k)与(k,j)已经被戳破了、再加上第 k 个气球的金币
- 使用记忆化搜索的方式递归获取结果,出口条件是:当 i>=j-1 时 dp[i][j]=0
- 时间复杂度:O(n^3)dp[0][n+1] 区间为 n^2、然后迭代 k 为 n 次,空间复杂度:O(n^2)
- 代码
private int solution(int[] nums) {
if (nums == null || nums.length <= 0) {
return 0;
}
int[] val = getValByNums(nums);
int valLen = val.length;
int[][] dp = new int[valLen][valLen];
for (int i = 0; i < valLen; i++) {
Arrays.fill(dp[i], -1);
}
recursionSearchDP(0, valLen-1, dp, val);
return dp[0][valLen - 1];
}
private int recursionSearchDP(int left, int right, int[][] dp, int[] val) {
if (left >= right - 1) {
return 0;
}
if (dp[left][right] >= 0) {
return dp[left][right];
}
for (int k = left + 1; k < right; k++) {
dp[left][right] = Math.max(dp[left][right], recursionSearchDP(left, k, dp, val)
+ recursionSearchDP(k, right, dp, val) + val[left]*val[k]*val[right]);
}
return dp[left][right];
}
private int[] getValByNums(int[] nums) {
int numLen = nums.length;
int[] val = new int[numLen + 2];
val[0] = 1;
for (int i = 0; i < numLen; i++) {
val[i+1] = nums[i];
}
val[val.length - 1] = 1;
return val;
}