力扣 416. 分割等和子集
最近遇到很多动态规划的题,之前也有做过一些,但是对于一些细节还是无法掌握,所以打算多练习一点动态规划的题。
给你一个 只包含正整数 的 非空 数组 nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11]
题目要得出的结论是:是否可以将数组分为两个元素和相等的子集,相当于计算一部分元素和,判断是否元素和可以等于总元素和的一半,又到了我们熟悉的选或不选的抉择。最简单暴力的还是dfs,就不写了,十有八九超时,直接按动态规划的思路去分析,可知以下结论。
1.计算出所有元素的和,若元素和为单数,则返回false,若元素中最大值,大于总元素和的一半,同样返回false;
2.转换公式:若选择当前元素,则值等于dp[i][j-nums[i]],选择的前提是j>=nums[i],若不选择,则值等于dp[i-1][j];
3.初始化:当target为0时,所有i都为true,因为可以不选择任何元素;当i为0时,只有num[0]的值为true,其他均为false。
万事俱备,码字。
public boolean canPartition(int[] nums) { int sum = 0; int max = nums[0]; for(int i : nums){ sum+=i; max=Math.max(max,i); } if((sum&1)==1||max>sum>>1){ return false; } int mid = sum>>1; int n = nums.length; boolean[][] dp = new boolean[n][mid+1]; for(int i =0;i<n;i++){ dp[i][0]=true; } dp[0][nums[0]]=true; for(int i=1;i<n;i++){ for(int j = 1;j<=mid;j++){ dp[i][j]=dp[i-1][j]; if(j-nums[i]>=0){ dp[i][j]=dp[i][j]||dp[i-1][j-nums[i]]; } } } return dp[n-1][mid]; }