1994. 好 子 集 的 数 目 \textcolor{Red}{1994. 好子集的数目} 1994.好子集的数目
给你一个整数数组 nums 。如果 nums 的一个子集中,所有元素的乘积可以表示为一个或多个 互不相同的质数 的乘积,那么我们称它为 好子集 。
-
比方说,如果
nums = [1, 2, 3, 4]:
[2, 3],[1, 2, 3]和[1, 3]是 好 子集,乘积分别为6 = 2*3,6 = 2*3和3 = 3。[1, 4]和[4]不是 好 子集,因为乘积分别为4 = 2*2和4 = 2*2。
请你返回 nums 中不同的 好 子集的数目对 109 + 7 取余 的结果。
nums 中的 子集 是通过删除 nums 中一些(可能一个都不删除,也可能全部都删除)元素后剩余元素组成的数组。如果两个子集删除的下标不同,那么它们被视为不同的子集
根据数据限制条件——nums[i]在【1,30】,主要小于32,说明可以使用32位标记
- 30以内的每个质数使用一个独立的位来唯一标记,比如——2(…01)、3(…010)…对于2*3=6利用如下表示(…011)表示使用2和3两个质数相乘得到。
- 特别的对于1,由于1不属于质数,且1与其他相乘不改变状态,于是将其表示为(…00)全零
- 对于题目要求的好子集,说明子集中每个位标记不重合,重合说明出现分解为两个相同质数的情况
然后基本解题思路在利用DP思想:对于i之前的所有有效组合(即之前所有可能的好子集的乘积的统计),利用i的位标记,查找不冲突的乘积组合,然后累加添加i的组合,同时保留原组合(也可以不添加i)
class Solution {
public int numberOfGoodSubsets(int[] nums) {
//mask保存1~30位标记,1-0,2-01,3-010,4-(-1)由于4包含重复质数...
int[] mask=new int[31];
int MOD=1000000007;
int cur=0;
for(int i=2;i<=30;i++){
for(int k=2;k<i;k++){
if(i%k==0){
//如果被可分解为不同质数的数分解,且分解后的数包含的质数不重合
//比如30=2*15(15=3*5与2不重合)-》30=2*3*5(...000111)
if(mask[i/k]!=-1&&mask[k]!=-1&&(mask[i/k]&mask[k])==0){
mask[i]=mask[i/k]|mask[k];
}
else{
mask[i]=-1;
}
break;
}
}
//如果为一个质数,赋予一个更高位位标记
//2-..01,3-..010,5-..0100,7-...
if(mask[i]==0){
mask[i]=1<<cur;
cur++;
}
}
Arrays.sort(nums);
Map<Integer,Integer> ans=new HashMap<>();
ans.put(0,1);
for(int i:nums){
if(mask[i]!=-1){
Set<Integer> k=ans.keySet();
List<Integer> keys=new ArrayList<>();
keys.addAll(k);
for(int key:keys){
if((key&mask[i])==0){
int mid=key|mask[i];
ans.put(mid,(ans.get(key)+ans.getOrDefault(mid,0))%MOD);
}
}
}
}
int res=0;
for(Map.Entry<Integer,Integer> e:ans.entrySet()){
if(e.getKey()!=0)
res=(res+e.getValue())%MOD;
}
return res;
}
}
结尾
题目来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems
⭐️关注作者,带你刷题,从简单的算法题了解最常用的算法技能(寒假每日一题)
⭐️关注作者刷题——简单到进阶,让你不知不觉成为无情的刷题机器,有问题请私信
该博客讨论了一道关于寻找数组中好子集(子集中所有元素乘积为不同质数的乘积)的问题。博主介绍了如何使用位标记和动态规划(DP)方法解决这个问题,其中涉及到对30以内质数的标记,以及如何检查子集中的质数不重合。通过遍历数组并更新有效组合,博主得出最终答案。文章适合对算法和质数分解有兴趣的读者。

被折叠的 条评论
为什么被折叠?



