题目链接
分割等和子集
题目描述
注意点
- 数组 nums 非空
- 数组 nums 只包含正整数
解答思路
- 最初想到的是根据回溯+剪枝解决本题,如果数组大小小于2,则肯定不能找到分割等和子集,除此以外,如果数组之和sum不能被2整除,也肯定不能找到分割等和子集,然后将数组排序后深度优先遍历查找符合的组合,最终运行时超时了,要考虑更加巧妙的方法
- 参照题解使用动态规划完成本题,其可以转换成背包问题,思路为:创建一个dp[n][target + 1]的数组,其中n为数组的长度,target为数组之和除以2,dp[i][j]表示(0, i)范围内能否选出某个数字组合之和为j,从dp[0][0]开始,找到从0~i个元素取数字组合时所有可能组成的数字之和的值,并将相应dp[i][j]更新为1,直到找到dp[i][target]为true或遍历完整个数组为止
代码
class Solution {public boolean canPartition(int[] nums) {int n = nums.length;if (n < 2) {return false;}int sum = 0;for (int num : nums) {sum += num;}if (sum % 2 != 0) {return false;}int target = sum / 2;// dp[i][j]表示(0, i)范围内能否选出某个数字组合之和为jboolean[][] dp = new boolean[n][target + 1];// 和为0必定成立for (int i = 0; i < n; i++) {dp[i][0] = true;}for (int i = 0; i < n; i++) {// 该元素为一组成立if (nums[i] == target) {return true;}// 第一个元素不必考虑前面(0, i - 1)的数字组合if (i == 0) {if (nums[i] < target) {dp[i][nums[i]] = true;}continue;}// 根据前面(0, i - 1)的数字组合推出(0, i)的数字组合之和for (int j = 1; j <= target; j++) {if (dp[i - 1][j] && j + nums[i] == target) {return true;}dp[i][j] = dp[i - 1][j];if (nums[i] < j) {dp[i][j] = dp[i][j] || dp[i - 1][j - nums[i]];}}}return false;}
}
关键点
- 将本题转换为背包问题
- 动态规划的思想
- 本题如何通过前面的情况动态规划推出后面的情况