动态规划part05
1.最后一块石头的重量
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头最小的可能重量。如果没有石头剩下,就返回 0。
解题思路:尽量将石头分成重量相同的两堆,相撞之后石头最小,
1dp[j] 表示容量为j的背包,最多可以最大重量为dp[j];
2.递推公式 dp[j] = Math.max(dp[j],dp[j-stones[i]]+stones[i])
3.01 背包每个石头只能放一次,物品遍历循环放在外层,遍历背包的for循环放在内层,倒序遍历;
4.最后dp[target]里的容量为target的背包所能背的最大重量,剩下的石头为(sum - dp[target]) - dp[target]
//一维数组
var lastStoneWeightII = function(stones) {
// stones = stones.sort((a,b)=>{return a-b});
let len = stones.length;
let sum = 0;
for (let i = 0; i < stones.length; i++) {
sum += stones[i];
}
let target = Math.floor(sum / 2);
let dp = new Array(target+1).fill(0);
for (let i = 0; i < len; i++) {
for (let j = target; j >= stones[i]; j--) {
dp[j] = Math.max(dp[j],dp[j-stones[i]]+stones[i])
}
}
return (sum - dp[target]) - dp[target];
};
//二维数组
// dp[i][j] 表示容量j的背包 从0-i中物品中取值,得到的最大价值
let dp = new Array(len).fill(0).map(()=>new Array(target + 1).fill(0));
for (let j = 0; j <= target ; j++) {
//初始化i = 0,第0行;
if (j >= stones[0]) {
dp[0][j] = stones[0];
}
}
for (let i = 1; i < len; i++) {
for (let j = 0; j <= target; j++) {
if (j >= stones[i]) {
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-stones[i]]+stones[i]);
}else{
dp[i][j] = dp[i-1][j];
}
}
}
console.log(dp[len-1][target]);
return (sum-dp[len-1][target]) - dp[len-1][target];
2.目标和
给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
示例:
输入:nums: [1, 1, 1, 1, 1], S: 3
输出:5
解释:
-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3
sum = right + left;
target = right - left;
left = (sum + target)/2 ,转换为在集合nums中找left的组合;
var findTargetSumWays = function(nums, target) {
nums = nums.sort((a,b)=>{return a-b});
let sum = 0;
let left = 0;
for (let i = 0; i < nums.length; i++) {
sum += nums[i];
}
if(Math.abs(target) > sum){
return 0;
}
if((sum + target) % 2 != 0){
return 0;
}
left = (target + sum) / 2;
// 遍历到i个数时,left为j时能填满背包的方法总数;
let dp = new Array(nums.length).fill(0).map(()=>new Array(left+1).fill(0));
// 初始化最上行(dp[0][j]),当nums[0] == j时(注意nums[0]和j都一定是大于等于零的,因此不需要判断等于-j时的情况),有唯一一种取法可取到j,dp[0][j]此时等于1
// 其他情况dp[0][j] = 0
for (let j = 0; j <= left; j++) {
if(nums[0] == j){
dp[0][j] = 1;
}
}
// 初始化最左列(dp[i][0])
// 当从nums数组的索引0到i的部分有n个0时(n > 0),每个0可以取+/-,
// 因此有2的n次方中可以取到j = 0的方案
// n = 0说明当前遍历到的数组部分没有0全为正数,因此只有一种方案可以取到j = 0(就是所有数都不取)
let numZeros = 0;
for(let i = 0; i < nums.length; i++) {
if(nums[i] == 0) {
numZeros++;
}
dp[i][0] = Math.pow(2, numZeros);
}
for (let i = 1; i < nums.length; i++) {
for (let j = 1; j <= left; j++) {
if (nums[i] > j) {
// nums[i]不可取
dp[i][j] = dp[i-1][j];
}else{
// nums[i]可取可不取
dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]];
}
}
}
return dp[nums.length-1][left];
};
1.确定dp数组的含义,dp[j]表示 填满 j 这么大容量的背包,有dp[j]种方法;
2.递推公式,只要搞到num[i],dp[j] 就有dp[j-nums[i]]种方法; 把所有的dp[j-nums[i]]累加起来 ,
dp[j] += dp[j-nums[i]];
3.初始化如果数组[0] ,target = 0,那么 bagSize = (target + sum) / 2 = 0。 dp[0]也应该是1, 也就是说给数组里的元素 0 前面无论放加法还是减法,都是 1 种方法。所以本题我们应该初始化 dp[0] 为 1。
4.遍历顺序,一维dp,nums外循环,target内循环,且内循环倒序;
5.打印dp数组;
//一维数组
var findTargetSumWays = function(nums, target) {
nums = nums.sort((a,b)=>{return a-b});
let sum = 0;
let left = 0;
for (let i = 0; i < nums.length; i++) {
sum += nums[i];
}
if(Math.abs(target) > sum){
return 0;
}
if((sum + target) % 2 != 0){
return 0;
}
left = (target + sum) / 2;
let dp = new Array(left + 1).fill(0);
dp[0] = 1;
for (let i = 0; i < nums.length; i++) {
for(let j = left;j >= 0;j--){
if (nums[i] > j) {
}else{
dp[j] = dp[j] + dp[j-nums[i]]
}
}
}
return dp[left];
}
3.一和零
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
var findMaxForm = function(strs, m, n) {
const dp = Array.from(Array(m+1), () => Array(n+1).fill(0));
let numOfZeros, numOfOnes;
for(let str of strs) {
numOfZeros = 0;
numOfOnes = 0;
for(let c of str) {
if (c === '0') {
numOfZeros++;
} else {
numOfOnes++;
}
}
for(let i = m; i >= numOfZeros; i--) {
for(let j = n; j >= numOfOnes; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i - numOfZeros][j - numOfOnes] + 1);
}
}
}
return dp[m][n];
};
let strs = ["10", "0001", "111001", "1", "0"];
let m = 5;
let n = 3;
findMaxForm(strs,m,n);