01背包问题
这个问题之前有讲到过,可以参考动态规划01背包问题
此次主要讲述01背包问题下的拓展出来的一些问题。
题目一、分割等和子集
题目链接:leetcode416.分割等和子集
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
这种题乍一眼看到感觉无从下手,其实不然。
思路
1.首先思考一下,既然要将它们分为相等的两份,那么他们的和必然是偶数,否则必定不会被分成相等的两份。
2.这个问题如何转换为01背包问题呢,其实很简单,首先确定什么是背包的容量,什么是物品的容量,什么又是物品的价值。
数组中的所有的数的和我们是可以求出来的,那么排除它是偶数后,它的一半也是能算出来的,也就是说,如果我用数组中这些数能得到总和的一半,那么就表示我一定有一种方法可以把它分为相等的两份。
假设数组所有数的和为偶数并记为sum,那么它的一半记为half_sum,那么它就是背包的容量。而整个数组既是物品的重量,也是物品的价值。
那么这道题就可以理解为,再容量为half_sum的大小下,我能装入的最大的物品价值是多少。
求出这个最优解后,只需要和half_sum比较是否相等即可。
递推公式
创建dp数组,用i表示物品数,j表示当前容量,dp[j]表示j容量下的最大总和。
初始化为0,因为只要当前总和为0,那么我总是可以获取到比0大的总和。
dp[j] = max(dp[j],dp[j - nums[i]] + nums[i])
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
//求和
for(int i = 0;i<nums.size();i++)
{
sum+=nums[i];
}
//判断是否为奇数
if(sum % 2 == 1) return false;
//获得总和的一半,也是背包的容量
int half_sum = sum/2;
vector<int