子集背包再进化--石子对撞问题
Posted C_YCBX Py_YYDS
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了子集背包再进化--石子对撞问题相关的知识,希望对你有一定的参考价值。
题目
题目解析
个人手写的详解,感觉已经比较详细了,接下来就是根据这个思路写下dp函数,利用dp背包分堆得到背包种的最大值,这是很多题目的惯用伎俩。此处的sum/2实际上无论sum是偶数还是奇数都不影响它的小的那一堆(反正sum/2是向下取整)
解题代码
二维数组形式
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
//accumulate函数是C++中的内置求和函数,三个参数,首地址~末地址+加法初始值
int sum = accumulate(stones.begin(),stones.end(),0);
//创建存储sum/2向下取整容量的背包,用于计算这个容量下最多装入多重的石子
//base case肯定是背包容量为0时是0,遍历方向不再过多强调了,可以自己想想二维数组(遍历方向的思考是压缩为一维的关键)
int dp[stones.size()+1][sum/2+1];
memset(dp,0,sizeof(dp));
for(int i=1;i<=stones.size();i++){
for(int j=1;j<=sum/2;j++){
//装
if(stones[i-1]<=j)
dp[i][j] = max(dp[i-1][j],dp[i-1][j-stones[i-1]]+stones[i-1]);
//不装
else
dp[i][j] = dp[i-1][j];
}
}
//得出A(偏大的)-B的结果
int B = dp[stones.size()][sum/2];
int A = sum-dp[stones.size()][sum/2];
return A-B;
}
};
压缩一维
由于dp关系仅仅和上一行数据相关,而且需要保证一直是上一行的数据,所以倒序遍历,当然倒序由base case来看也是合法的顺序。
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
//accumulate函数是C++中的内置求和函数,三个参数,首地址~末地址+加法初始值
int sum = accumulate(stones.begin(),stones.end(),0);
int dp[sum/2+1];
memset(dp,0,sizeof(dp));
for(int i=1;i<=stones.size();i++){
for(int j=sum/2;j>=1;j--){
//装
if(stones[i-1]<=j)
dp[j] = max(dp[j],dp[j-stones[i-1]]+stones[i-1]);
//不装
else
dp[j] = dp[j];
}
}
//得出A(偏大的)-B的结果
int B = dp[sum/2];
int A = sum-dp[sum/2];
return A-B;
}
};
以上是关于子集背包再进化--石子对撞问题的主要内容,如果未能解决你的问题,请参考以下文章
代码随想录算法训练营第四十二天 | 01背包问题,你该了解这些01背包问题,你该了解这些 滚动数组 416. 分割等和子集
LeetCode 446. 等差数列划分 II - 子序列(动态规划)/ 416. 分割等和子集 (背包问题)/322. 零钱兑换(完全背包)
动态规划第六篇:01背包问题(分割等和子集 + 最后一块石头的重量 II)