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;
} }
}

补充另一种写法:

 public boolean canPartition(int[] nums) {
int sum = ; for (int num : nums) {
sum += num;
} if ((sum & ) == ) {
return false;
}
sum /= ; int n = nums.length;
boolean[][] dp = new boolean[n+][sum+];
for (int i = ; i < dp.length; i++) {
Arrays.fill(dp[i], false);
} dp[][] = true; for (int i = ; i < n+; i++) {
dp[i][] = true;
}
for (int j = ; j < sum+; j++) {
dp[][j] = false;
} for (int i = ; i < n+; i++) {
for (int j = ; j < sum+; j++) {
if (j-nums[i-1] >= 0) {
dp[i][j] = (dp[i-1][j] || dp[i-1][j-nums[i-1]]);
}
}
} return dp[n][sum];
}

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

leetcode416的更多相关文章

  1. [Swift]LeetCode416. 分割等和子集 | Partition Equal Subset Sum

    Given a non-empty array containing only positive integers, find if the array can be partitioned into ...

  2. 通过netty把百度地图API获取的地理位置从Android端发送到Java服务器端

    本篇记录我在实现时的思考过程,写给之后可能遇到困难的我自己也给到需要帮助的人. 写的比较浅显,见谅. 在写项目代码的时候,需要把Android端的位置信息传输到服务器端,通过Netty达到连续传输的效 ...

随机推荐

  1. JavaScript之作用域

    1. 作用域 变量能够起作用的范围,作用域分为全局作用域和局部作用域 全局作用域: 1. 最外层函数或者在最外层函数外通过var定义的变量: 2. window对象的属性和方法具有全局作用域: 3. ...

  2. python基础08_set集合

    关于前几次课的回顾: #!/usr/bin/env python # coding:utf-8 ## 字符串 数字 列表 元组 字典 ## 可变:列表 字典 ## 不可变:字符串, 数字, 元组 na ...

  3. vim语法

    在任何目录输入命令“vim <文件名>”就可以使用vim编辑文件了. 打开vim后,默认的是命令模式,输入i进入编辑模式,这个时候你可以编辑文件了.在编辑模式下按Esc键回到命令模式,在命 ...

  4. yarn依赖管理工具的使用

    Yarn是Facebook发布的一款依赖管理工具,它比npm更快.更高效. 与NPM命令对照 npm install => yarn install npm install --save [pa ...

  5. 1.5 pycharm使用

    1.5 pycharm使用 前言    在写脚本之前,先要找个顺手的写脚本工具.python是一门解释性编程语言,所以一般把写python的工具叫解释器.写python脚本的工具很多,小编这里就不一一 ...

  6. C++常见的概念

    1)多态:允许将子类类型的指针赋值给父类类型的指针.赋值以后,父类对象可以根据当前赋值给它的子类对象的特性以不同的方式运作. 2)深拷贝:重新分配内存:浅拷贝:共用同一内存. 3)友元:非成员函数不能 ...

  7. python学习之路03

    一.常量和变量 1.python中的数据类型 分类: ​ Number:数字型[整型,浮点型,复数] ​ String:字符串型 ​ Boolean:布尔型[True,False] ​ None:空值 ...

  8. _proto_ && prototype (原型 && 原型链)

    原型一直都是JavaScript基础里面的痛点,因为在JavaScript里面没有类的概念,都是通过原型对象来实现继承,下面的这个图很好的说明几者之间的关系! a.__proto__ = A.prot ...

  9. Python3之max key参数学习记录

    今天用Python写脚本,想要实现这样的功能:对于给定的字典,返回其中Value最大值对应的Key. 搜索后找到了解决方法,同时也学到了max key参数的作用. 例1, testlist = [9. ...

  10. java_GC

    垃圾回收1    内存分配    垃圾回收    调用垃圾回收器    对象终结            调用垃圾回收器        System.gc()和Runtime.getRuntime(). ...