LeetCode 312. 戳气球(动态规划解决)
Posted 数据结构和算法
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 312. 戳气球(动态规划解决)相关的知识,希望对你有一定的参考价值。
截止到目前我已经写了 500多道算法题,其中部分已经整理成了pdf文档,目前总共有1000多页(并且还会不断的增加),大家可以免费下载
下载链接:https://pan.baidu.com/s/1hjwK0ZeRxYGB8lIkbKuQgQ
提取码:6666
比如上图中3和5本来是不相邻的,但当我们戳破1的时候,3和5变成了相邻,所以我们可以使用一个list把每个气球的值都存起来,戳破的时候就把他从list中给删除。来看下代码
public int maxCoins(int[] nums) {
List<Integer> list = new LinkedList<>();
//先把nums数组中的元素放到list中
for (int num : nums) {
list.add(num);
}
return helper(list);
}
public int helper(List<Integer> list) {
//如果是空,则表示没有气球了,直接返回0
if (list.isEmpty())
return 0;
int max = 0;
int size = list.size();
for (int i = 0; i < size; ++i) {
//戳破当前气球所获得的硬币数量
int sum = 0;
if (size == 1) {
//如果只有一个气球,直接戳破他获取的硬币就是
//当前气球上的数字
sum = list.get(i);
} else if (size == 2) {
//如果有两个气球,戳破任何一个气球都是他俩的乘积
sum = list.get(0) * list.get(1);
} else {
//如果是3个和3个以上的气球要注意戳破第1个和最后一个气球
//的边界条件
if (i == 0) {
sum = list.get(i) * list.get(i + 1);
} else if (i == size - 1) {
sum = list.get(i) * list.get(i - 1);
} else {
sum = list.get(i - 1) * list.get(i) * list.get(i + 1);
}
}
//把当前气球戳破了,就把他从list中移除
Integer num = list.remove(i);
//记录最大的值,sum表示戳破当前气球所得到的硬币数,
//helper(list)表示戳破剩下的气球所获得的硬币数
max = Math.max(max, sum + helper(list));
//把当前气球添加进list,尝试戳破下一个气球
list.add(i, num);
}
return max;
}
public int maxCoins(int[] nums) {
int length = nums.length;
//申请一个比原来长度大2的临时数组
int[] temp = new int[length + 2];
//临时数组的首尾值赋为1,中间的值和nums的值一样
for (int i = 1; i <= length; i++)
temp[i] = nums[i - 1];
temp[0] = temp[length + 1] = 1;
return helper(temp, 0, length + 1);
}
//戳破开区间(left,right)中所有气球所获得的最大硬币
public int helper(int[] temp, int left, int right) {
//如果(left,right)之间没有气球,返回0,表示没有任何气球可戳破
if (left + 1 == right)
return 0;
int res = 0;
//在(left,right)中我们尝试戳破每一个位置的气球,取最大值即可
for (int i = left + 1; i < right; ++i) {
int sum = temp[left] * temp[i] * temp[right] + helper(temp, left, i) + helper(temp, i, right);
res = Math.max(res, sum);
}
return res;
}
这样当数据量比较大的时候还是会超时,因为这里包含了大量的重复计算,我们还可以使用一个map,把计算的结果存到map中,下次使用的时候如果计算过了,就直接从map中取,来看下代码
public int maxCoins(int[] nums) {
int length = nums.length;
//申请一个比原来长度大2的临时数组
int[] temp = new int[length + 2];
//临时数组的首尾值赋为1,中间的值和nums的值一样
for (int i = 1; i <= length; i++)
temp[i] = nums[i - 1];
temp[0] = temp[length + 1] = 1;
int[][] map = new int[length + 2][length + 2];
return helper(map, temp, 0, length + 1);
}
public int helper(int[][] map, int[] temp, int left, int right) {
//如果(left,right)之间没有气球,返回0,表示没有任何气球可戳破
if (left + 1 == right)
return 0;
//如果已经计算过了,就直接从map中取
if (map[left][right] > 0)
return map[left][right];
int res = 0;
for (int i = left + 1; i < right; ++i) {
int sum = temp[left] * temp[i] * temp[right] + helper(map, temp, left, i) + helper(map, temp, i, right);
res = Math.max(res, sum);
}
//把计算的结果在存储到map中
map[left][right] = res;
return res;
}
for (int i = length - 1; i >= 0; i--) {
for (int j = i + 2; j <= length + 1; j++) {
//计算戳破开区间(i,j)中间的气球所获取的最大硬币数
……
}
}
如果一个气球被戳破后,本来不挨着的两个气球可能变成挨着的了,这样还是不好计算。我们可以这样来计算,在开区间(i,j)中随便找一个气球,假如是k,我们让k成为最后一个被戳破的气球,那么这样就简单了,当我们戳破气球k的时候所获得的最大硬币是
nums[i]*nums[k]*nums[j];
如下图所示
这个k可以是开区间(i,j)中的任何一个气球,取最大的即可,所以我们可以找出递推公式,在开区间(i,j)中戳破第k个气球所获得硬币数
sum=temp[i]*temp[k]*temp[j]+dp[i][k]+dp[k][j];
其中dp[i][k]表示戳破开区间(i,k)中的气球所获得的最大硬币数量,同理dp[k][j],来看下代码
public int maxCoins(int[] nums) {
int length = nums.length;
//申请一个比原来长度大2的临时数组
int[] temp = new int[length + 2];
//临时数组的首尾值赋为1,中间的值和nums的值一样
for (int i = 1; i <= length; i++)
temp[i] = nums[i - 1];
temp[0] = temp[length + 1] = 1;
//dp[i][j]表示戳破开区间(i,j)之间的气球所获得的最大硬币数
int[][] dp = new int[length + 2][length + 2];
for (int i = length - 1; i >= 0; i--) {
for (int j = i + 2; j <= length + 1; j++) {
//遍历开区间(i,j)中的每一个气球,尝试戳破他所获得的最大硬币
for (int k = i + 1; k < j; k++) {
//递推公式,戳破气球k可以获得temp[i] * temp[k] * temp[j]个硬币,
//dp[i][k]表示戳破开区间(i,k)之间的气球所获得的硬币数
//dp[k][j]表示戳破开区间(k,j)之间的气球所获得的硬币数
int sum = temp[i] * temp[k] * temp[j] + dp[i][k] + dp[k][j];
//保留最大的
dp[i][j] = Math.max(dp[i][j], sum);
}
}
}
return dp[0][length + 1];
}
以上是关于LeetCode 312. 戳气球(动态规划解决)的主要内容,如果未能解决你的问题,请参考以下文章