01 背包
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
二维dp数组01背包
定义:dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
递推公式:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
初始化:首先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0。
使用:可以先遍历物品,再遍历背包容量
// weight数组的大小 就是物品个数
for(int i = 1; i < weight.size(); i++) { // 遍历物品for(int j = 0; j <= bagweight; j++) { // 遍历背包容量if (j < weight[i]) dp[i][j] = dp[i - 1][j];else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);}
}
一维dp数组(滚动数组)
把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);与其把dp[i - 1]这一层拷贝到dp[i]上,不如只用一个一维数组了,只用dp[j](一维数组,也可以理解是一个滚动数组)。
定义:dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。
递推公式:
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
初始化:全赋值为0即可,不会覆盖递推结果。
递推顺序:逆序,保证物品i只放入一次。
for(int i = 0; i < weight.size(); i++) { // 遍历物品for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);}
}
416. 分割等和子集
给你一个 只包含正整数 的 非空 数组
nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。示例 1:
输入:nums = [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11]
此题能够通回溯算法进行枚举子序列进行判断是否有子序列之和等于sum/2。
01背包问题,题目中物品是nums[i],重量是nums[i],价值也是nums[i],背包体积是sum/2,能否找到能装满背包的选取序列。如果nums[target]==target,集合中的子集总和正好可以凑成总和target。
bool canPartition(int* nums, int numsSize){//dp[i]中的i表示背包内总和int sum=0;for(int i=0;i<numsSize;++i){sum+=nums[i];}if(sum%2==1) return false;int target=sum/2;int dp[10002]={0};for(int i=0;i<numsSize;++i){for(int j=target;j>=nums[i];j--){dp[j]=fmax(dp[j],dp[j-nums[i]]+nums[i]);}}if(dp[target]==target) return true;return false;
}
1049. 最后一块石头的重量 II
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头最小的可能重量。如果没有石头剩下,就返回 0。
示例:
- 输入:[2,7,4,1,8,1]
- 输出:1
本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小。
dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j]。 target=sum/2,要求的是能够装满target的最大重量。
int lastStoneWeightII(int* stones, int stonesSize) {int sum=0;for(int i=0;i<stonesSize;++i){sum+=stones[i];}int target=sum/2;int dp[15001]={0};for(int i=0;i<stonesSize;++i){for(int j=target;j>=stones[i];--j){dp[j]=fmax(dp[j],dp[j-stones[i]]+stones[i]);}}return (sum-dp[target])-dp[target];
}