LeetCode 416.分割等和子集 | 类0-1背包问题 | 解题思路及代码

Given a nonempty array nums, which only contains positive number. Find if the array can be divided into two parts such that the sum of each part is equal.

Input: nums = [1,5,11,5]
Output: true
Explanation: divide into [1,5,5] and [11].

To satisfy two part sum are equal, then each part sum is half of total sum. If the total sum is an odd or the max number in the array is bigger than half of total sum, then it’s can’t satisfy the requirement.

This problem is similar to 0-1 package problem. What differs is that the total sum we chose can’t exceed the capacity of the package in 0-1 package problem, while the chosen number’s sum must equal to half of total sum in this question. Similar to traditional 0-1 package problem, we can use dynamic programming to solve this problem.

We use a two-dimensional array boolean dp[n][half+1] for dynamic programming. Where n is the length of nums and half is the half of total sum. We define dp[i][j]:

  • i ranges from 0 to n-1, which means the subset(0, i) of nums.

  • j ranges from 0 to half, which means if the subset(0, i) can constitute a sum of value j.

dp[i][j] is true if subset(0, i) of nums can constitute a sum of value j. Then dp[n-1][half] means in the whole array nums, if it can constitute a sum of half.

The state transition equation is as follows:

[dp[i][j]=\begin{cases} dp[i-1][j]\ ||\ dp[i-1][j-num[i]]&num[i]\leq j\ dp[i-1][j]&num[i]>j \end{cases}]

dp[i][j] means we are deciding if we count nums[i] to constitute a sum of j for subset(0, i).

The upper case means: nums[i]<=j< code>, if we not choose this number, then our result(can constitute or not) is equal to "if the subset(0, i-1) can constitute a sum of <code>j</code>", that is<code>dp[i-1][j]</code>. If we choose this number, then our result is equal to "if the subset(0, i-1) can constitute a sum of <code>j-num[i]</code>". Either of them is true, then we can constitute a sum of <code>j</code> by subset(0, i) of <code>nums</code>.<br>The lower case means: <code>nums[i]>j</code>, the number is bigger than the number(<code>j</code>) we want to constitute, so we can't choose. Our result is same as <code>dp[i-1][j]</code>.<!--=j<-->

Observe the transition equation, we found that each line of our two-dimensional array dp only related to the previous line(dp[i] is decided by dp[i-1] only). So we can store the dp in only one line:
When we handle the new line i, the 1-dimensional array stores the i-1 line data! For each line, we ranges j from half to 0. The new state transition equation is as follows:

[dp[j]=dp[j]\ ||\ dp[j-nums[i]] ]

Since j ranges from half to 0, dp[j] or dp[j-nums[i] store the date of last line(i-1). After each round of j, we updated a line.

If nums.length<2< code> or <code>sum(nums)</code> is odd or <code>max(nums)>sum(nums)/2</code>, return <code>false</code>.<br>Create a boolean array <code>dp[sum(nums)/2+1]</code>. For <code>i</code> in range from 0 to <code>num.length</code>, for <code>j</code> in range from <code>sum(nums)/2</code> to 0, if <code>dp[j]==true</code> or <code>dp[j-nums[i]==true</code>, the <code>dp[j]=true</code>.<br>Finally, return <code>dp[sum(nums)/2]</code>.<!--2<-->

The detailed code is as follows

public class p416 {
    public static boolean canPartition(int[] nums) {
        int len = nums.length;
        if (len < 2) {
            return false;
        }
        int totalSum = 0, half = 0, maxNum = 0;
        for (int num : nums) {
            totalSum += num;
            maxNum = Math.max(maxNum, num);
        }
        if (totalSum % 2 != 0 || totalSum / 2 < maxNum) {
            return false;
        }
        half = totalSum / 2;
        boolean[] dp = new boolean[half + 1];
        dp[0] = true;
        for (int i = 0; i < len; i++) {
            int num = nums[i];
            for (int j = half; j >= num; j--) {
                dp[j] = dp[j] | dp[j - num];
            }
        }
        return dp[half];
    }
}

Time complexity: The outer loop nums.length, the inner loop sum(nums)/2, so the total time complexity is O(nums.length &#xD7; sum(nums)/2)
Space complexity: After optimization, the space we used are sum(nums)/2, so the total space complexity is O(sum(nums)/2)

Original: https://www.cnblogs.com/liuzhch1/p/16197049.html
Author: liuzhch1
Title: LeetCode 416.分割等和子集 | 类0-1背包问题 | 解题思路及代码

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/620672/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 动态格子算法

    动态格子算法常用于弹幕游戏的碰撞检测优化,可减少遍历开销。 概述 动态格子算法常用于弹幕游戏的碰撞检测优化,可减少遍历开销。这是我之前做的小游戏就用到了此算法,当后期满屏子弹时,优…

    数据结构和算法 2023年6月8日
    089
  • liquibase新增字段注释导致表格注释同时变更bug记录

    liquibase是一个用于数据库变更跟踪、版本管理和自动部署的开源工具。它的使用方式方法可以参考官方文档或者其他人的博客,这里不做过多介绍。 1. 问题复现 在使用过程中发现了一…

    数据结构和算法 2023年6月8日
    0108
  • 剑指 Offer 51. 数组中的逆序对

    剑指 Offer 51. 数组中的逆序对最容易想到的做法当然是暴力的枚举每一组数对,((nums[i], nums[j])),时间复杂度为(O(n^2)),对应数据范围为(5e4)…

    数据结构和算法 2023年6月7日
    0108
  • 二叉树——判断是否是搜索二叉树和完全二叉树

    1.1. 问题 给定二叉树的一个头节点 head,已知其中没有重复值的节点,实现两个函数分别判断这棵二叉树是否为搜索二叉树和完全二叉树。 1.2. 思路 判断是否是搜索二叉树可用中…

    数据结构和算法 2023年6月10日
    089
  • 特殊字符HTML/markdown

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    数据结构和算法 2023年6月7日
    094
  • Java实现负载均衡算法–轮询和加权轮询

    1.普通轮询算法 轮询(Round Robin,RR)是依次将用户的访问请求,按循环顺序分配到web服务节点上,从1开始到最后一台服务器节点结束,然后再开始新一轮的循环。这种算法简…

    数据结构和算法 2023年6月16日
    081
  • 背包问题

    01背包问题 有n个物品和一个容量为v的背包,每一个物品有两个属性(体积v,价值w),每件物品最多只用一次。在所有物品中选择的物品总体积最小(小于背包容量),价值最大。 利用动态规…

    数据结构和算法 2023年6月8日
    096
  • 数据库索引的基石—-B树

    数据结构相对来说比较枯燥, 我尽量用最易懂的话,来把B树讲清楚。学过数据结构的人都接触过一个概念—-二叉树。简单来说,就是每个父节点最多有两个子节点。为了在二叉树上更快…

    数据结构和算法 2023年6月8日
    081
  • win10下apache superset的使用

    一、环境准备 安装python3即3.4以上版本 二、python创建一个虚拟环境用来作为superset的容器 创建虚拟环境:-(1)virtualenv env_superse…

    数据结构和算法 2023年6月8日
    068
  • Timer和ScheduledThreadPoolExecutor的区别及源码分析

    Timer 基于单线程、系统时间实现的延时、定期任务执行类。具体可以看下面红色标注的代码。 Timer延时、定时任务的实现采用单线程,在主循环(mainLoop)中循环遍历任务队列…

    数据结构和算法 2023年6月8日
    081
  • 多重背包 II —— 二进制优化

    核心思想 (前提建议先把多重背包朴素版算法搞清楚 ) 转换成0 ~ 1背包问题,对个体拆分后全部打散,反正能够保证从全局上使得所有情况依然存在就可以了。 举例分析(以下都使用该样例…

    数据结构和算法 2023年6月7日
    083
  • 深度学习——正则化

    深度学习——正则化 作者:Oto_G 全是自我理解,表达不严谨,仅供参考 本文默认正则化范数为L1范数 这是今天讨论的问题: 为什么融入正则的损失函数能够防止过拟合 为什么正则融入…

    数据结构和算法 2023年6月12日
    0111
  • 树-堆排序

    堆排序 堆排序基本介绍 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为 O(nlogn),它也是不稳定排序。 堆是具有以下…

    数据结构和算法 2023年6月12日
    083
  • Java 中HashMap详解(含HashTable, ConcurrentHashMap)

    本篇重点: 1.HashMap的存储结构 2.HashMap的put和get操作过程 3.HashMap的扩容 4.关于transient关键字 5.HashMap, HashTa…

    数据结构和算法 2023年6月12日
    077
  • 1047 Student List for Course (25 分)

    1. 题目 Zhejiang University has 40,000 students and provides 2,500 courses. Now given the re…

    数据结构和算法 2023年6月7日
    0103
  • 严格次小生成树

    洛谷最优解rank1,u1s1快是真的快,好写是真的好写,理解也很好理解 简直酸爽,舒服了 非原创,仅作解释 严格次小生成树,顾名思义,权值仅仅小于最小生成树 重点解释这个查询 i…

    数据结构和算法 2023年6月7日
    075
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球