class Solution {
    public boolean canPartition(int[] nums) {  
     int sum=0;  
     for (int num:nums) sum+= num;
     if(sum % 2 == 1) return false;
     else{  
        sum /=2;
        int n=nums.length;  
        // dp[i][j] 表示 如果我们取前i个数字,且背包容量为j的情况下,最多能放入多少东西
        int dp[][]=new int[n][sum + 1];  
        // dp[0][0] 为初始状态,表示,没有任何没有东西没有体积,其余部分初始化
        for(int i=nums[0];i<=sum;i++){
             dp[0][i] = nums[0];
        }
        //遍历n个数字,即视为n个产品
        for(int i=1;i<n;i++){  
            //加入了这种物品后更新状态
            for(int j=nums[i];j<=sum;j++){  
                dp[i][j]=Math.max(dp[i-1][j], dp[i-1][j-nums[i]]+nums[i]);  
            }  
        }  
        //放满了才能表示正好1/2
        if(dp[n-1][sum]==sum) 
            return true;  
        else
            return false;  
     }  

 }
}

 补充另一种写法:

 1 public boolean canPartition(int[] nums) {
 2     int sum = 0;
 3     
 4     for (int num : nums) {
 5         sum += num;
 6     }
 7     
 8     if ((sum & 1) == 1) {
 9         return false;
10     }
11     sum /= 2;
12 
13     int n = nums.length;
14     boolean[][] dp = new boolean[n+1][sum+1];
15     for (int i = 0; i < dp.length; i++) {
16         Arrays.fill(dp[i], false);
17     }
18     
19     dp[0][0] = true;
20     
21     for (int i = 1; i < n+1; i++) {
22         dp[i][0] = true;
23     }
24     for (int j = 1; j < sum+1; j++) {
25         dp[0][j] = false;
26     }
27     
28     for (int i = 1; i < n+1; i++) {
29         for (int j = 1; j < sum+1; j++) {
30             if (j-nums[i-1] >= 0) {
31                 dp[i][j] = (dp[i-1][j] || dp[i-1][j-nums[i-1]]);
32             }
33         }
34     }
35    
36     return dp[n][sum];
37 }

 

参考:https://leetcode.com/problems/partition-equal-subset-sum/discuss/90592/01-knapsack-detailed-explanation

先上一张图:测试数据为nums=[1,3,3,5],判断是否可以分割为两个和为6的数组(不要求连续)。

下面解释一下思路,初始化二维数组dp,初始化全部为false,这个数组中的每一个元素表示:

在前i个元素中,任选其中0~i个元素(可以一个不选,也可以全都选),这些元素的和,是否恰好等于j。

具体来说,dp[0][0]表示前0个元素是否可以组成和为0的情况,这作为前提条件,设置为true。

除这个元素之外的第一列:

dp[1][0],表示前1个元素是否可以组成和为0的情况。答案是:可以组成。只要不选择任何元素,其和值就是0。

dp[2][0],表示前2个元素是否可以组成和为0的情况。答案是:可以组成,只要不选择任何元素,其和值就是0.

dp[3][0],dp[4][0]也是同样道理,均为true。

除dp[0][0]之外的第一行:

dp[0][1],表示前0个元素是否可以组成和为1的情况。答案是:不可以。前0个就是没有任何元素,其和不可能大于0.

dp[0][2]……dp[0][6]也是同样道理,均为false。

 

下面从dp[1][1]开始判断,一行一行的判断。如图绿色的行。

如果这个元素“上面”是true,那么当前元素就是true。表示当前元素不被选择,可以直接组成和i-1个元素是一样的值。

如果当前元素的列标j>=nums[i-1],则进一步判断。如图,就是比较每一个单元格的“上标注”是否大于等于“右标注”。

如果满足条件,则进一步判断:当前行的上一行的[上标-右标]的元素是否是true。

按照这个转移条件,一行一行的判断,最右下角的元素就是所求结果。表示前i个元素,是否可以组成sum值。

 

通过分析,可以发现,只要“最后一列”,出现过一次true,那么最终的结果就一定是ture,也就可以提前停止循环了。

具体来说,上图蓝色行的最后一列,即dp[3][6]是true,那么其下面的元素肯定都是true。也就无需后面的判断了。这样理论上可以提高效率。

posted on 2018-10-14 00:25  Sempron2800+  阅读(181)  评论(0编辑  收藏  举报