Leetcode 416. 分割等和子集 背包问题变种 动态规划
/* * @lc app=leetcode.cn id=416 lang=cpp * * [416] 分割等和子集 * * https://leetcode-cn.com/problems/partition-equal-subset-sum/description/ * * algorithms * Medium (49.73%) * Likes: 761 * Dislikes: 0 * Total Accepted: 122.1K * Total Submissions: 245.5K * Testcase Example: '[1,5,11,5]' * * 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 * * * * 示例 1: * * * 输入:nums = [1,5,11,5] * 输出:true * 解释:数组可以分割成 [1, 5, 5] 和 [11] 。 * * 示例 2: * * * 输入:nums = [1,2,3,5] * 输出:false * 解释:数组不能分割成两个元素和相等的子集。 * * * * * 提示: * * * 1 * 1 * * */
思路:
可以转化为背包问题,即,能否得到sum/2
dp[i][j]表示使用0-(i-1)的物品,得到和为j.
base状态为dp[i][0]=true,因为sum=0肯定都可以实现
dp[i][j]=dp[i-1][j](不放入nums[i])|dp[i-1][j-nums[i]](放入nums[i])
class Solution { public: bool canPartition(vector<int>& nums) { int sum=0; for(int num:nums) sum+=num; if(sum%2!=0) return false; sum/=2; int n=nums.size(); vector<vector<int>> dp(n+1,vector<int>(sum+1,false)); for(int i=0;i<=n;++i) dp[i][0]=true; for(int i=1;i<=n;++i){ for(int j=1;j<=sum;++j){ if(j<nums[i-1]) dp[i][j]=dp[i-1][j]; else dp[i][j]=dp[i-1][j]|dp[i-1][j-nums[i-1]]; } } return dp[n][sum]; } };
进阶:
状态压缩:dp[i][j]
都是通过上一行dp[i-1][..]
转移过来的,之前的数据都不会再使用了
所以,我们可以进行状态压缩,将二维dp
数组压缩为一维
只在一行dp
数组上操作,i
每进行一轮迭代,dp[j]
其实就相当于dp[i-1][j]
,所以只需要一维数组就够用了。
唯一需要注意的是j
应该从后往前反向遍历,因为每个物品(或者说数字)只能用一次,以免之前的结果影响其他的结果
也就是i=n时,dp[j]为n-1的状态,又因为更新状态时需要用到dp[j-nums[i-1]],所以从后往前更新状态,保证用到dp时是i-1的原值,而不是更新后的值
class Solution { public: bool canPartition(vector<int>& nums) { int sum=0; for(int num:nums) sum+=num; if(sum%2!=0) return false; sum/=2; int n=nums.size(); vector<bool> dp(sum+1,false); dp[0]=true; for(int i=1;i<=n;++i){ for(int j=sum;j>=0;--j){ if(j<nums[i-1]) continue; else dp[j]=dp[j-nums[i-1]]|dp[j]; } } return dp[sum]; } };
联系方式:emhhbmdfbGlhbmcxOTkxQDEyNi5jb20=