leetcode-1994:好子集的数目

简介: leetcode-1994:好子集的数目

题目

题目链接

给你一个整数数组 nums 。如果 nums 的一个子集中,所有元素的乘积可以表示为一个或多个 互不相同的质数 的乘积,那么我们称它为 好子集 。

  • 比方说,如果 nums = [1, 2, 3, 4] :
    [2, 3] ,[1, 2, 3] 和 [1, 3] 是 好 子集,乘积分别为 6 = 23 ,6 = 23 和 3 = 3 。
    [1, 4] 和 [4] 不是 好 子集,因为乘积分别为 4 = 22 和 4 = 22 。
    请你返回 nums 中不同的 好 子集的数目对 109 + 7 取余 的结果。

nums 中的 子集 是通过删除 nums 中一些(可能一个都不删除,也可能全部都删除)元素后剩余元素组成的数组。如果两个子集删除的下标不同,那么它们被视为不同的子集。

示例 1:

输入:nums = [1,2,3,4]
输出:6
解释:好子集为:
- [1,2]:乘积为 2 ,可以表示为质数 2 的乘积。
- [1,2,3]:乘积为 6 ,可以表示为互不相同的质数 2 和 3 的乘积。
- [1,3]:乘积为 3 ,可以表示为质数 3 的乘积。
- [2]:乘积为 2 ,可以表示为质数 2 的乘积。
- [2,3]:乘积为 6 ,可以表示为互不相同的质数 2 和 3 的乘积。
- [3]:乘积为 3 ,可以表示为质数 3 的乘积。

示例 2:

输入:nums = [4,2,3,15]
输出:5
解释:好子集为:
- [2]:乘积为 2 ,可以表示为质数 2 的乘积。
- [2,3]:乘积为 6 ,可以表示为互不相同质数 2 和 3 的乘积。
- [2,15]:乘积为 30 ,可以表示为互不相同质数 2,3 和 5 的乘积。
- [3]:乘积为 3 ,可以表示为质数 3 的乘积。
- [15]:乘积为 15 ,可以表示为互不相同质数 3 和 5 的乘积。

解题

方法一:状态压缩DP

参考链接

class Solution {
public:
    int MOD=1e9+7;
    vector<int> p={2,3,5,7,11,13,17,19,23,29};
    int numberOfGoodSubsets(vector<int>& nums) {
        int n=nums.size();
        vector<int> cnts(31,0); //题目规定数值相同,下标不同均视为不同方案,因此要统计每个数出现次数
        for(int num:nums) cnts[num]++;
        int mask=1<<10;
        vector<long> f(mask);//利用位来记录每个质数使用状态,以此进行状态压缩
        f[0]=1;
        for(int i=2;i<=30;i++){
            if(cnts[i]==0) continue;
            int cur=0,x=i;
            // 对 i 进行试除
            bool ok=true;
            for(int j=0;j<10;j++){
                int t=p[j],c=0;
                while(x%t==0){
                    cur|=(1<<j);
                    x/=t;
                    c++;
                }
                // 如果 i 能够被同一质数试除多次,说明 i 不能加到子集,跳过
                if(c>1){
                    ok=false;
                    break;
                }
            }
            if(!ok) continue;
            // 枚举前一状态 prev
            //(确保考虑一个新数值 i 时,依赖的子集 prev 存储的为尚未考虑 i 的方案数)
            for(int prev=0;prev<mask;prev++){
                // 只有当前选择数与前一状态不冲突,则能够进行转移,将方案数进行累加
                if((prev&cur)!=0) continue;
                f[prev|cur]=(f[prev|cur]+f[prev]*cnts[i])%MOD;
            }
        }
        long res=0;
        // 统计所有非空集的方案数(因为题目要求 子集数)
        for(int i=1;i<mask;i++) res=(res+f[i])%MOD;
        // 在此基础上,考虑每个 1 选择与否对答案的影响
        for(int i=0;i<cnts[1];i++) res=res*2%MOD;
        return res;
    }
};


相关文章
|
10月前
|
Go
【LeetCode 热题100】DP 实战进阶:最长递增子序列、乘积最大子数组、分割等和子集(力扣300 / 152/ 416 )(Go语言版)
本文深入解析三道经典的动态规划问题:**最长递增子序列(LIS)**、**乘积最大子数组** 和 **分割等和子集**。 - **300. LIS** 通过 `dp[i]` 表示以第 `i` 个元素结尾的最长递增子序列长度,支持 O(n²) 动态规划与 O(n log n) 的二分优化。 - **152. 乘积最大子数组** 利用正负数特性,同时维护最大值与最小值的状态转移方程。 - **416. 分割等和子集** 转化为 0-1 背包问题,通过布尔型 DP 实现子集和判断。 总结对比了三题的状态定义与解法技巧,并延伸至相关变种问题,助你掌握动态规划的核心思想与灵活应用!
395 1
|
算法 Go
🚀 力扣热题 78:子集(详细解析)
✅ 回溯法:经典通用模板,逻辑清晰易扩展。 ✅ 二进制法:简洁高效,适合面试快速写出解法。
432 30
LeetCode第90题子集II
LeetCode第90题"子集II"的解题方法,通过排序和回溯算法生成所有不重复的子集,并使用一个boolean数组来避免同一层中的重复元素,展示了解决这类问题的编码技巧。
LeetCode第90题子集II
LeetCode第78题子集
文章分享了LeetCode第78题"子集"的解法,使用递归和回溯算法遍历所有可能的子集,展示了将子集问题视为树形结构进行遍历的解题技巧。
|
Python
【Leetcode刷题Python】416. 分割等和子集
LeetCode 416题 "分割等和子集" 的Python解决方案,使用动态规划算法判断是否可以将数组分割成两个元素和相等的子集。
271 1
|
索引 Python
【Leetcode刷题Python】78. 子集
LeetCode题目78的Python编程解决方案,题目要求给定一个互不相同的整数数组,返回该数组所有可能的子集(幂集),且解集中不能包含重复的子集。
221 1
|
存储 算法
经典的滑动窗口的题目 力扣 2799. 统计完全子数组的数目(面试题)
经典的滑动窗口的题目 力扣 2799. 统计完全子数组的数目(面试题)
220 0
2670.找出不同元素数目差数组-力扣(LeetCode)
2670.找出不同元素数目差数组-力扣(LeetCode)
179 0
|
机器学习/深度学习 存储 算法
LeetCode题目 90:五种算法 回溯\迭代\位掩码\字典树\动态规划实现 子集ll
LeetCode题目 90:五种算法 回溯\迭代\位掩码\字典树\动态规划实现 子集ll
|
存储 机器学习/深度学习 算法
力扣78题:生成子集
力扣78题:生成子集