代码随想录算法训练营第三十三天| 1049. 最后一块石头的重量 II 494. 目标和 474.一和零
1049. 最后一块石头的重量 II
思路:
因为含有两个石头的相撞,所以需要把dp的目标值改成sum/2,
然后取得这个目标值的最大值,然后对sum-2*target
代码:
1 // 要求:有多个石头,两两撞击,取得剩下的石头的最小值 2 // ——》一定要碰到最后一个 3 // 注意: 4 // 1,x==y: 两个粉碎,x<y: y = y-x 5 // 2,可能剩下一个或者0个 那么怎么判断是0还是1? 6 // 7 // 背包问题: 8 // dp[n]:当有N个物品的时候,当前重量的最小值 9 // 10 // 11 // 怎么设置? 已经直到重量是这些,但是终止条件是什么? 12 // 13 // 物品:石头 价值:大小,重量 个数 14 // 容量:物品的数量 15 // 16 // dp[n]: 撞的话需要取两个石头,但是当前遍历的只有一个石头 17 // 1,不撞 dp[n] 18 // 2,撞: dp[n] = dp[n+1] - (2x)[第一个是遍历y,使得x属于<=y] 19 // 20 21 // 22 // 如果涉及两个东西之间的比较,可以往容量为sum/2那里思考 23 // dp[n]: 当容量为N 时候的最大值 24 // 25 int lastStoneWeightII(vector<int>& stones) { 26 if (stones.size() == 1) return stones[0]; 27 28 int sum_ = 0; 29 for (int num : stones) 30 { 31 sum_ += num; 32 } 33 34 vector<int> dp((sum_ / 2)+1, 0); 35 36 for (int i = 0; i < stones.size(); i++) 37 { 38 for (int j = (sum_ / 2); j >= stones[i]; j--) 39 { 40 dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]); 41 } 42 } 43 44 int result = sum_ - 2 * dp[sum_ / 2]; 45 if (result < 0) return 0; 46 else return result; 47 }
494. 目标和
要求:
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
思路:
因为是正负,所以只要满足了正的数,那么也就可以满足所有的和了
因此 l + r = sum l-r = target -> l = (sum+target)/2
如果 l 不可以取整,那么就是无解
dp[n] 定义:当和为N时,有多少种求和方法
递推公式
dp[n] = dp[0] + dp[1] +...... dp[n]
=> dp[n]+=dp[j-nums[i]] 含义:dp[n]+ 当我取nums[i]时,可以有dp[j-nums[i]]种方法凑成J
遍历顺序
先遍历物品,再倒序遍历容量
代码
1 // 对NUMS 数组前面加 +-,从而让这个值可以等于target,返回有多少种这样的表达式 2 // 3 // 问题: 4 // 1,统计的是装满的方法 5 // 2,每个nums都要被用到,而不是可以选择 6 // 7 // 背包问题:在背包的容量情况下,最大装满,同时它的价值最大 ——》让这些数字尽量装满target —》 要知道的是装满的有几种方法 8 // 9 // 让每个值都可以 + -,然后在每次更改状态的时候,记录一下看看他们能否装满? 10 // 11 // 新思路: 12 // 正负改成两个阵营, l = (sum+target)/2 如果不能整除,那么就返回0 13 // dp[n]: 有几种方法 正数的值 满足N ——————》》 这个是个难题 14 // dp[n] = dp[0~n-1] 15 // 16 int findTargetSumWays(vector<int>& nums, int target) { 17 18 int sum_ = accumulate(nums.begin(), nums.end(), 0); 19 if (sum_ < abs(target)) return 0; 20 if ((sum_ + target) % 2 != 0) return 0; 21 22 int bgSize = (sum_ + target) / 2; 23 vector<int>dp(bgSize + 1, 0); 24 dp[0] = 1; 25 26 //dp[n] 和满足N的时候有几种组合方式 27 for (int i = 0; i < nums.size(); i++) 28 { 29 for (int j = bgSize; j >= nums[i]; j--) 30 { 31 dp[j] = dp[j] + dp[j - nums[i]]; 32 } 33 } 34 35 return dp[bgSize]; 36 }
474.一和零
要求:
输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。
注意:
需要知道每个字符串的长度,同时选择的这几个字符串需要满足0 1 的个数满足 m n
转换为背包问题
物品:strs
容量: m n
dp[m][n] 在 m n 的个数下,数量最大的情况
因为是数量,所以物品的value 统统为1
递推公式:
dp[x][y] = max(dp[x][y], 1+ dp[x-m_][y-n_]);
代码:
1 // 要求:找出strs中找到一个子集,让所有这些子集的0和1的数目是多少 2 // 3 // 难点: 4 // 知道每个字符串0 1 的数目之后,然后匹配,让个数最大 5 // 6 // dp[m][n] 当 MN 下最长的子集数目 7 // 8 // dp[x][y] = 9 // 1,不选 dp[x][y] 10 // 2,选 dp[x][y] = 1 + dp[x-m][y-n] 11 // 两个选则最大值 ——》其中因为是个数,所以他们的value 为1 12 // 13 int findMaxForm(vector<string>& strs, int m, int n) { 14 vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); 15 16 dp[0][0] = 0; 17 for (int i = 0; i < strs.size(); i++) 18 { 19 int m_ = 0, n_ = 0; 20 for (char c : strs[i]) 21 { 22 if (c == '0') m_++; 23 else if (c == '1') n_++; 24 } 25 26 for (int x = m; x >= m_; x--) 27 { 28 for (int y = n; y >= n_; y--) 29 { 30 dp[x][y] = max(dp[x][y], 1 + dp[x - m_][y - n_]); 31 } 32 } 33 34 } 35 36 return dp[m][n]; 37 }