首页 > 其他分享 >子集和加总问题(从洛谷博客同步)

子集和加总问题(从洛谷博客同步)

时间:2024-06-10 10:44:09浏览次数:11  
标签:顺序 删除 从洛谷 值域 复杂度 leq 子集 加总 考虑

给出 \(\{a_{1\dots n}\}\),找出一个子集和为 \(0\)。

这是 NPC 的,当 \(|a_i| \leq n\) 的时候可以 \(n^3\) 背包,当然地可以使用 bitset 压位至 \(\frac {n^3} w\)。

值域还是太难受了,考虑怎么压下来值域,因为和为 \(0\),值域又是 \(n\),通过调整顺序总是存在一种方案使得值域在 \([-n,n]\)。但是我们不知道这个顺序,但感觉存太大值域又没用。

所以随机一个顺序,可以证明这个时候值域期望是不大于 \(O(n^{1.5})\) 的,复杂度可以做到 \(O(\frac {n^{2.5}} w)\)。

还能再给力一点吗?

时间复杂度 \(O(nA)\),空间线性的 01 背包可行性及方案构造(Pisinger, 1999)

因为在很多情况下值域开大到 \(nA\) 是很浪费的,中途偏差其实和 \(s\) 差距不会太大——也总是存在一种顺序使得中途偏差不超过 \(A\),前面考虑找到这样的顺序,但是发现不太现实,所以现在来考虑另一种:边加边删。

我们先随便找到一个和与 \(s\) 差不超过 \(A\) 的集合,然后接下来 加入其它数或者删除已经加入的数,那么这样我们考虑的值域就是 \(O(A)\) 的了。

但是删除确实是一件很困难的事情,并不是所有数都能删,我们不能删除之前没加进来的数,我们也不能记录目前集合。

但是如果只在 dp 里面放一个可行性,那属实是有点浪费我们这么小的值域,而且如果要把可删除的信息放在状态里,也没法压缩复杂度。

下面是 Pisinger 最天才的想法:

于是我们来考虑记录可以删除的前缀。

\(f_{i,j}\) 表示前 \(i\) 个数,和 \(j\) 的最大可删除前缀(保证是满的)的长度!

(若和无法为 \(j\) 则设为 \(-1\))

设初始集合为 \(a_1\dots a_x\)。

初始值为

\(f_{x,a_k} = x\)。

考虑转移:

加入一个数

\[f_{i,j} = \max\{f_{i-1,j},f_{i-1,j-a_i}\} \]

这很好理解——加入一个数就是普通的背包,求 \(\max\) 形式。

\[f_{i,j - a_k} = \max \{f_{i,j-a_k} , k - 1\} \space (1\leq k \leq f_{i,j}) \]

循环顺序为从大到小做(删了这个数还可能删下一个数)。

虽然有些抽象,但是还是比较好理解:枚举一个可以删去的数字——然后删掉,转移。

但是这样还是 \(O(n^2A)\) 的啊——确实,因为要删很多不同的数。

所以我们给 \(k\) 设置下界 \(f_{i-1,j}< k \leq f_{i,j}\) 。

因为之前能删除的已经考虑过了——删除某些数后转移到了另一个值,所以我们只用考虑之前没考虑过的值即可了!因为 \(f\) 值对于每一个固定的和都是不减的,所以复杂度为 \(O(nA)\)!

删前缀为什么对?

考虑最终状态下的后半部分选入的集合一定被 dp 决策考虑了,而前面每步一定删除到了能删除的最大位置(这个前缀一定能被拆成若干段删除或不删除的区间),如果存在合法方案,一定会被这个过程包含在内。

想看正确性严谨的证明可以去翻论文。

代码并不复杂,即使需要构造方案也可以在 20 行之内完成,在 OI 中可能有用。


参考

标签:顺序,删除,从洛谷,值域,复杂度,leq,子集,加总,考虑
From: https://www.cnblogs.com/Dreamerkk/p/18240465

相关文章

  • 一般图边覆盖计数(从洛谷博客同步)
    今天模拟赛中出现了一个题,需要对一个\(n\)个点,\(m\)条边的图做边覆盖计数,边覆盖是一个边集\(S\subseteqE\)使得任意一个点\(i\)都存在一条边\((u,v)\inE\)满足\(u=i\)或\(v=i\),即覆盖所有的点。\(n\leq40,m\leq60\),1s512M。然后被我使用神秘做法冲过去了(然后莫......
  • 09_分割等和子集
    416.分割等和子集题目难易:中等给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。注意:每个数组中的元素不会超过100数组的大小不会超过200示例1:输入:[1,5,11,5]输出:true解释:数组可以分割成[1,5,5]和[11].......
  • 代码随想录训练营第28天 | 93.复原IP地址、78.子集 、90.子集II
    93.复原IP地址本期本来是很有难度的,不过大家做完分割回文串之后,本题就容易很多了题目链接/文章讲解:https://programmercarl.com/0093.复原IP地址.html视频讲解:https://www.bilibili.com/video/BV1XP4y1U73i/和分割字符串类似,还有判断当前数字是否符合要求functionisValid......
  • leetCode.90. 子集 II
    leetCode.90.子集II题目思路代码classSolution{public:vector<vector<int>>res;vector<int>path;vector<vector<int>>subsetsWithDup(vector<int>&nums){//先排序,让有相同元素的都放到一起sort(nums.be......
  • day44 01背包问题 416. 分割等和子集
    背包问题01背包有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],得到的价值是value[i]。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。暴力的解法每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么......
  • 代码随想录算法训练营第四十四天|01 背包、动态规划:01背包理论基础(滚动数组)、416. 分
    01背包文档讲解:代码随想录题目链接:46.携带研究材料(第六期模拟笔试)有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],得到的价值是value[i]。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。 暴力解法:每个物品都有放与不放两种状态......
  • 力扣-416. 分割等和子集
    1.题目题目地址(416.分割等和子集-力扣(LeetCode))https://leetcode.cn/problems/partition-equal-subset-sum/题目描述给你一个只包含正整数的非空数组 nums。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 示例1:输入:nums=[1,5,11,5]......
  • 416. 分割等和子集
    给你一个只包含正整数的非空数组nums。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。示例1:输入:nums=[1,5,11,5]输出:true解释:数组可以分割成[1,5,5]和[11]。示例2:输入:nums=[1,2,3,5]输出:false解释:数组不能分割成两个元素和相等的......
  • 42天【代码随想录算法训练营34期】第九章 动态规划part04(● 01背包问题,你该了解这些!
    **416.分割等和子集**classSolution:defcanPartition(self,nums:List[int])->bool:_sum=0dp=[0]*10001fornuminnums:_sum+=numif_sum%2==1:returnfalsetarget=......
  • 78. 子集-c++
    给你一个整数数组nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。你可以按任意顺序返回解集。示例1:输入:nums=[1,2,3]输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]示例2:输入:nums=[0]输出:[[],[0]]classSolution{......