LeetCode每日一题,416. Partition Equal Subset Sum
先看题目描述
题目大意就是给定一个只包含正整数的非空数组,让我们判断是否可以将这个非空数组分割成两个子集
算法和思路
动态规划
一开始看到这题没有头绪,然后去看题解,发现这题可以转化为「0-1 背包问题」使用动态规划解决
算法流程:
- 状态定义:dp[i][j] 表示从数组的[i, …, len - 1] 区间挑选一些正整数,能否使这些数的和恰好等于 j。能的话 dp[i][j] = true;否则为 false
- 状态转移方程:很多时候,状态转移方程思考的角度是分类讨论,对于「0-1 背包问题」而言就是「当前考虑到的数字选与不选」
- 不选择 nums[i] 的话,dp[i][j] = dp[i + 1][j]
- 选择 nums[i] 的话,dp[i][j] = dp[i + 1][j - nums[i]]
- 故状态转移方程为 $dp[i][j] = dp[i + 1][j]\quad or \quad dp[i + 1][j - nums[i]]$
- 考虑初始化条件:
- j - nums[i] 作为数组下标,故必须保证 $j \geq nums[i]$
- 虽然 i 的范围是从 0 到 len - 1,但考虑到状态转移方程,要用到下一行的状态,我们定义 dp 数组的行数为 len + 1,并初始化 dp[len][0] = true,用于刚开始的状态转移
- 返回 dp[0][target],其中 target 为数组元素之和除 2(若数组元素之和不能被 2 整除,则直接返回 false)
优化
我们之前定义的 dp 数组的行数是 len + 1,列数为 target + 1,但我们观察状态方程,可以发现某一行的状态只与其下一行有关,那么我们没必要维护一个 len + 1 行的 dp 数组,可以只维护一个 2 行的 dp 数组,这样可以节省空间
题解中虽然也是使用的动态规划,但状态的定义不太一样,题解中 dp[i][j] 表示的是从从数组的 [0, …, i] 区间挑选一些正整数,能否使这些数的和恰好等于 j。能的话 dp[i][j] = true;否则为 false,最后返回 dp[len - 1][target] 。两种定义都可以,基本的思路还是一致的
算法源码
动态规划
1 |
|
优化空间后的动态规划
1 | class Solution { |