算法萌新如何学好动态规划(3)( 六 )


个 , 问是否存在一种选取方案 , 使其满足某种条件 。 或者是否存在一种选取方案 , 使其满足某种条件的同时 , 得到某种参数的最值 。
这样归纳可能还是过于抽象 , 因此我们将在下文「习题练习」中讲解一些具体的题型来帮助大家理解 。
算法萌新如何学好动态规划(3)文章插图
习题练习416. 分割等和子集题目描述给定一个只包含正整数的非空数组 。 是否可以将这个数组分割成两个子集 , 使得两个子集的元素和相等 。
示例 1输入: [1, 5, 11, 5]输出: true解释: 数组可以分割成 [1, 5, 5] 和 [11].?示例 2输入: [1, 2, 3, 5]输出: false解释: 数组不能分割成两个元素和相等的子集.提示

  • 每个数组中的元素不会超过 100
  • 数组的大小不会超过 200
解题思路「将数组分割成两个子集 , 使得两个子集的元素和相等」 , 假设数组元素个数为N , 元素和为 M , 则问题可以转化为「现有 N 个数字 , 是否可以选取若干个数字 , 使其和为 M/2」 。
进行这一步转换之后 , 则可以较为明显的看出该问题本质上是从 N 物品中选取若干个物品 , 即「背包问题」 。 并且每个物品只能选一次 , 因此是「0/1 背包」模型 。
仿照「0/1 背包」模型 , 我们可以定义
算法萌新如何学好动态规划(3)文章插图
为仅考虑前 i 个物品 , 是否存在一种选择方案使其数值和为 j 。 我们规定若
算法萌新如何学好动态规划(3)文章插图
则表示存在一种方案;若
算法萌新如何学好动态规划(3)文章插图
则表示不存在可行方案 。
因此我们可以得到如下「DP 转移方程」:
算法萌新如何学好动态规划(3)文章插图
定义一下初始条件 ,
算法萌新如何学好动态规划(3)文章插图
。 接下来直接套用「0/1 背包」滚动数组版本的代码即可完成本题 , 具体细节见下述代码 。
C++ 代码实现class Solution {public:vector f;bool canPartition(vectorfor(int item:nums) sum += item;if(sum % 2 != 0) return 0;sum /= 2;// 开辟存储空间f.resize(sum+1);f[0] = 1;// DP 过程for(int i = 1; i <= nums.size(); i++)for(int j = sum; j >= nums[i-1]; j--)if(f[j-nums[i-1]] == 1)f[j] = 1;if(f[sum]) return 1;else return 0;}};494. 目标和?题目描述给定一个非负整数数组 ,
算法萌新如何学好动态规划(3)文章插图
和一个目标数 , 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?一共有5种方法让最终目标和为3 。 提示
  • 数组非空 , 且长度不会超过 20。
  • 初始的数组的和不会超过 1000。
  • 保证返回的最终结果能被 32 位整数存下 。
解题思路假设整个数组元素和为 sum , 且开头符号为 + 的数字和为 a , 则整个开头符号为 - 的数字和为 sum - a 。 因此若要使数组和为 S , 则 a - (sum - a) = S , 即 2*a = S + sum 。


推荐阅读