关于此题我的往期文章:
LeetCode 416.分割等和子集(动态规划【0-1背包问题】采用一维数组dp:滚动数组)_呵呵哒( ̄▽ ̄)"的博客-CSDN博客
https://heheda.blog.csdn.net/article/details/133212716看本期文章时,可以先回顾一下动态规划入门知识和完全背包理论和实战:
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等

最优的子结构性质,这是解决动态规划问题的关键。最优解可以从其子问题的最优解构造出来。如何将问题分解成子问题?
(1)递归
![dfs(i,c) = max(dfs(i-1,c),dfs(i-1,c-nums[i])+nums[i]);](https://1000bd.com/contentImg/2024/03/24/d86191ff54432ba3.png)
- class Solution {
- public:
- // 递归 超出时间限制!
- bool canPartition(vector<int>& nums) {
- int n=nums.size(),sum=0;
- for(const int& x:nums) sum+=x;
- if(sum % 2 == 1) return false;
- int left = sum/2; // 目标和
- function<int(int,int)> dfs=[&](int i,int c) -> int {
- if(i<0) {
- if(c==0) return 0;
- else return INT_MIN;
- }
- if(c
return dfs(i-1,c); - return max(dfs(i-1,c),dfs(i-1,c-nums[i])+nums[i]);
- };
- int ans = dfs(n-1,left);
- return ans<0?false:true;
- }
- };
(2)递归搜索 + 保存计算结果 = 记忆化搜索
- class Solution {
- public:
- // 记忆化搜索
- bool canPartition(vector<int>& nums) {
- int n=nums.size(),sum=0;
- for(const int& x:nums) sum+=x;
- if(sum % 2 == 1) return false; // 如果 sum 为奇数,直接返回 false
- int left = sum/2,memo[n+1][left+1];
- memset(memo,-1,sizeof(memo));
- function<int(int,int)> dfs=[&](int i,int c) -> int {
- if(i<0) {
- if(c==0) return 0;
- else return INT_MIN;
- }
- int &res = memo[i][c];
- if(res != -1) return res;
- if(c
return res=dfs(i-1,c); - return res=max(dfs(i-1,c),dfs(i-1,c-nums[i])+nums[i]);
- };
- int ans = dfs(n-1,left);
- return ans<0?false:true;
- }
- };
(3)1:1 翻译成递推
![dfs(i,c) = max(dfs(i-1,c),dfs(i-1,c-nums[i])+nums[i]);](https://1000bd.com/contentImg/2024/03/24/d86191ff54432ba3.png)
![f[i][c] = max(f[i-1][c]),f[i-1][c-nums[i]+nums[i]);](https://1000bd.com/contentImg/2024/03/24/4128be5a81b5115d.png)
![f[i+1][c] = max(f[i][c]),f[i][c-nums[i]+nums[i]);](https://1000bd.com/contentImg/2024/03/24/fdc6a9cbc9ff4cb9.png)
初始化:根据递归的边界来初始化
- if(i<0) {
- if(c==0) return 0;
- else return INT_MIN;
- }
返回最终结果:根据 dfs(n-1,left) 翻译, f[n][left]
- class Solution {
- public:
- // 递推
- bool canPartition(vector<int>& nums) {
- int n=nums.size(),sum=0;
- for(const int& x:nums) sum+=x;
- if(sum % 2 == 1) return false; // 如果 sum 为奇数,直接返回 false
- int left = sum/2,f[n+1][left+1];
- memset(f,128,sizeof(f)); // 无穷小
- f[0][0]=0;
- for(int i=0;i
- for(int c=0;c<=left;c++) {
- if(c < nums[i]) f[i+1][c] = f[i][c];
- else f[i+1][c] = max(f[i][c],f[i][c-nums[i]]+nums[i]);
- }
- }
- int ans=f[n][left];
- return ans<0 ? false:true;
- }
- };
(4)空间优化:两个数组(滚动数组)
- f[(i+1)%2][c] = max(f[i%2][c],f[i%2][c-nums[i]]+nums[i]);

- class Solution {
- public:
- // 递推 + 优化空间(二维)
- bool canPartition(vector<int>& nums) {
- int n=nums.size(),sum=0;
- for(const int& x:nums) sum+=x;
- if(sum % 2 == 1) return false; // 如果 sum 为奇数,直接返回 false
- int left = sum/2,f[2][left+1];
- memset(f,128,sizeof(f)); // 无穷小
- f[0][0]=0;
- for(int i=0;i
- for(int c=0;c<=left;c++) {
- if(c < nums[i]) f[(i+1)%2][c] = f[i%2][c];
- else f[(i+1)%2][c] = max(f[i%2][c],f[i%2][c-nums[i]]+nums[i]);
- }
- }
- int ans=f[n%2][left];
- return ans<0 ? false:true;
- }
- };
(5)空间优化:一个数组
- f[i+1][c] = max(f[i][c]),f[i][c-nums[i]+nums[i]);
- f[c] = max(f[c],f[c-nums[i]]+nums[i]);
- class Solution {
- public:
- // 递推 + 优化空间(一维)
- bool canPartition(vector<int>& nums) {
- int n=nums.size(),sum=0;
- for(const int& x:nums) sum+=x;
- if(sum % 2 == 1) return false; // 如果 sum 为奇数,直接返回 false
- int left = sum/2,f[left+1];
- memset(f,128,sizeof(f)); // 无穷小
- f[0]=0;
- for(int i=0;i
- for(int c=left;c>=nums[i];c--) {
- f[c] = max(f[c],f[c-nums[i]]+nums[i]);
- }
- }
- int ans=f[left];
- return ans<0 ? false:true;
- }
- };
-
-
- 可以改成
- for(const int& x:nums) {
- for(int c=left;c>=x;c--) {
- f[c] = max(f[c],f[c-x]+x);
- }
- }
参考和推荐文章:
利用memset 赋值无穷大和无穷小_如何使用memset函数初始化数组的值为无穷小_Prudento的博客-CSDN博客
https://blog.csdn.net/Prudento/article/details/123534212416. 分割等和子集 - 力扣(LeetCode)
https://leetcode.cn/problems/partition-equal-subset-sum/solutions/442412/shou-hua-tu-jie-fen-ge-deng-he-zi-ji-dfshui-su-si-/
常用技巧:
- memset(a,127,sizeof(a));
- 即得到无穷大。
- memset(a,128,sizeof(a));
- 即得到无穷小,与上述的值互为相反数。
- memset(a,60,sizeof(a));
- 即近似为第一个式子的数值的一半。
- memset(a,0,sizeof(a));赋值0
- memset(a,-1,sizeof(a));赋值-1
- 上述例子对于a数组为int或long long时,成立。
-
- memset( , 0x3f , sizeof );
- 特意去试了下,发现 0x3f3f3f3f 真的是个非常精巧的常量
-
- 来自这篇博客:https://blog.csdn.net/Prudento/article/details/123534212
-
相关阅读:
XML配置文件
基于准确度评估的7自由度手术机器人术前摆位优化算法
notepad++下载 地址真实有效
计算机组成原理-第六章 总线【期末复习|考研复习】
【CANN训练营】CANN昇腾体验官2022第二季第五期 轻松应对5道题(不轻松)
JetBrains ReSharper Ultimate 2023.2.2
物联网对接协议
机器人工程的工作与考研之困惑“卷”补充
自从学会了 CSS resize 属性,我也可以对美女背景大展身手
UVM实战——02构建一个简单的UVM平台_1 UVM平台中的关键组件
-
原文地址:https://blog.csdn.net/weixin_41987016/article/details/134224630