zoukankan      html  css  js  c++  java
  • leetcode416

    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。也就无需后面的判断了。这样理论上可以提高效率。

  • 相关阅读:
    mysql 查询优化 ~ select count 知多少
    mongodb 案例 ~ 经典故障案例
    printk 驱动调试
    21天学通C++学习笔记(七):函数
    OPC UA
    MQTT
    分库分表
    水平、垂直权限问题(横向越权与纵向越权)
    数据库中的行转列和列转行
    面试知识点
  • 原文地址:https://www.cnblogs.com/asenyang/p/9784914.html
Copyright © 2011-2022 走看看