专栏:50多种数据结构彻底征服

专栏:50多种经典图论算法全部掌握

最近一网友发文称,大四没课想请假去实习,结果学校不批,引来其他网友的各种吐槽。其实我对这件事也不能理解,我认为大四就是应该去找工作,去实习的,如果没课还留在学校干啥呢?

记得十多年前我大四的时候,同学基本上都快跑光了,走的时候也没和任何人说,毕业答辩的时候回来就行了。如果他直接走,不和学校说估计会好一些,但每个学校的制度又不太一样,有的管的严,有的管的松,不能一概而论。

打开网易新闻 查看更多图片
打开网易新闻 查看更多图片
打开网易新闻 查看更多图片
打开网易新闻 查看更多图片
打开网易新闻 查看更多图片
打开网易新闻 查看更多图片

--------------下面是今天的算法题--------------

来看下今天的算法题,这题是LeetCode的第416题:分割等和子集。

问题描述

来源:LeetCode第416题

难度:中等

给你一个只包含正整数的非空数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例1:

输入:nums = [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11] 。

示例2:

输入:nums = [1,2,3,5] 输出:false 解释:数组不能分割成两个元素和相等的子集。

  • 1 <= nums.length <= 200

  • 1 <= nums[i] <= 100

问题分析

这题是让判断能不能把数组分成两部分,并且这两部分的和都相等。其实这是一道典型的01背包问题,关于 01背包,完全背包和多重背包,以及它们的公式推导和状态压缩 ,我们在 中都有过详细讲解,这里就不在重复介绍。

我们来看下这题使用01背包该怎么解,首先要计算数组中所有元素的和sum,这个和必须是偶数,数组才 有可能 被分成相等的两部分,否则没法分,并且每一部分的值都是sum/2。那么这题就变成了从数组中选择一些数字,每个数字最多只能选择一次,判断它们的和是否等于sum/2,这就是01背包问题了。

关于01背包的详细介绍可以看下中的第11章动态规划部分,下面我们使用的是状态压缩后的代码,要注意第二个for循环一定要逆序。还有这两个for循环的位置不能调换,因为背包问题实际上是一个组合问题。

JAVA:

public boolean canPartition(int[] nums) {     int sum = 0;// 计算所有数字的和     for (int num : nums)         sum += num;     if ((sum & 1) == 1)// 如果和是奇数,不能分为两部分         return false;     // 转化为01背包问题,从数组中选择一些数字,     // 判断他们的和是否等于target。     int target = sum >> 1;     boolean[] dp = new boolean[target + 1];     dp[0] = true;     // 对于01背包的优化这里是先遍历物品在遍历容量,但要注意第二个     // for循环一定要逆序。     for (int num : nums) {         for (int i = target; i > 0; i--)// 这里一定要逆序             if (i >= num)                 dp[i] = dp[i] || dp[i - num];     }     return dp[target]; }

C++:

public:     bool canPartition(vector

  &nums) {         int sum = 0;// 计算所有数字的和         for (auto &num: nums)             sum += num;         if (sum & 1)// 如果和是奇数,不能分为两部分             return false;         // 转化为01背包问题,从数组中选择一些数字,         // 判断他们的和是否等于target。         int target = sum >> 1;         vector

  dp(target + 1, 0);         dp[0] = true;         // 对于01背包的优化这里是先遍历物品在遍历容量,但要注意第二个         // for循环一定要逆序。         for (auto &num: nums) {             for (int i = target; i > 0; i--)// 这里一定要逆序                 if (i >= num)                     dp[i] = dp[i] || dp[i - num];         }         return dp[target];     }

Python:

def canPartition(self, nums: List[int]) -> bool:     sums = sum(nums)  # 计算所有数字的和     if sums % 2:  # 如果和是奇数,不能分为两部分         return False     '''      转化为01背包问题,从数组中选择一些数字,      判断他们的和是否等于target。     '''     target = sums // 2     dp = [True] + [False] * target     '''     对于01背包的优化这里是先遍历物品在遍历容量,但要注意第二个     for循环一定要逆序。     '''     for i, num in enumerate(nums):         for j in range(target, num - 1, -1):  # 这里一定要逆序             dp[j] |= dp[j - num]     return dp[target]

笔者简介

博哥,真名:王一博,毕业十多年, 作者,专注于 数据结构和算法 的讲解,在全球30多个算法网站中累计做题2000多道,在公众号中写算法题解800多题,对算法题有自己独特的解题思路和解题技巧,喜欢的可以给个关注,也可以 下载我整理的1000多页的PDF算法文档 。