1994. 好子集的数目
题目描述
给一个整数的数组nums,数组中的元素不超过30,数组长度不超过
给出了好子集的定义(数组中所有元素成绩可以拆分为互不相同质数的积)
对好子集个数的要求是不同子集删除的下标不同,那么也被视为不同的子集
问给定数组nums的好子集的个数?
f1-状态压缩+动态规划 |
基本分析
- 直观的看,哪些元素不能放到好子集中去?是能整除某个平方数,例如4,8,9
- 进一步细想,能在1-30之间,作为构建子集的质数有哪些?1+10个质数
- 通过以上分析,想到怎么定义dp状态?f[i][state]为考虑范围在[1,30]的前i个数,并且好子集拆解结果用到的质数为state时候的方案数
- 初始状态怎么定义?f[1][0]=1, 表示当只有数值1的话,只有空集是合法的
- 接下来怎么考虑转移?
- 不考虑数字i,对应f[i][s] = f[i-1][s]
- 考虑数字i,这个时候又有情况需要区分
- 数字i可以不能分解为几个不同质数相乘,去换下一个数字i
- 数字i可以分解成几个不同质数相乘(对应为sub)。遍历所有可以转移的前一个状态prev(prev和sub的&是0)来尝试转移
- 对1的情况怎么特殊处理?可以把1的次数当做系数考虑
代码
class Solution: def numberOfGoodSubsets(self, nums: List[int]) -> int: ps = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] mod = 10**9+7 cnt = Counter(nums) nmask = 1<<10 # 定义dp状态 # mask取0-nmask-1,i取0-30 f = [[0]*nmask for _ in range(31)] # 取前1个数字,mask为0的方案个数是1 f[1][0] = 1 for i in range(2, 31): for mask in range(nmask): f[i][mask] = f[i-1][mask] if cnt[i] == 0: continue flag = True cur = 0 for j, p in enumerate(ps): if i % (p*p) == 0: flag = False break if i % p == 0: cur |= 1<<j if not flag: continue for prev in range(0, nmask): if prev & cur != 0: continue f[i][prev|cur] = (f[i][prev|cur] + f[i-1][prev]*cnt[i]) % mod ans = 0 for mask in range(1, nmask): ans = (ans + f[30][mask]) % mod for i in range(cnt[1]): ans = ans * 2 % mod return ans
复杂度
时间:
空间:
总结
这个题应该没有完全吃透,后面还要回顾。
怎么把nums和质数数组对应起来?题目给了nums的取值范围是1-30,就是暗示了质数可以取其中的10个,进而哪些质数取不取就可以用二进制的mask表示。
关于枚举每个数和mask的关系,这里有些存疑,为什么要先遍历某个数i,然后遍历所有的mask?从常规定义看是考虑1-30的前i个数,且对应的质数分解为mask时的方案总数,从这个角度看也许解释的通
某个数的质数分解方面:这里的代码写的复杂了,更好的写法是试除法
赋值问题:不管这个i是不是能分解成质数,需要先把不考虑i的情况转移过来f[i][s] = f[i-1][s]
转移写法:这里是枚举之前值prev,保证prev和cur不重
边界问题:统计最终结果的时候,不能把mask=0考虑进去,但是在dp之前,必须给出f[1][0]=1的边界,这个怎么理解?
系数问题:对所有合法的结果,可以考虑cnt[1]个位置的1取或者不取都有2种可能,对应的系数是
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理