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 的乘积。

提示:

1 <= nums.length <= 105
1 <= nums[i] <= 30

状压dp

    /**
    dp[i]表示状态为i时的好子集数。
    这里的关键点有两个:
    一个是状态转移公式 dp[maskForNum|state]+=cnt[num]*dp[state],
    还有一个是根据状态转移公式确定如何初始化dp数组
     */
    private final int mod=1000000007;
    public int numberOfGoodSubsets(int[] nums) {
        long res=0;
        int[] prime={2,3,5,7,11,13,17,19,23,29};
        long[] dp=new long[1024];
        //根据状态转移公式dp[maskForNum|state]=(dp[maskForNum|state]+cnt[num]*dp[state])反推dp数组如何初始化
        dp[0]=1;
        int[] cnt=new int[31];
        for(int num:nums) cnt[num]++;
        //遍历nums中的好数,1之后单独考虑
        for(int num=2;num<=30;++num){
            if(cnt[num]==0||num%4==0||num%9==0||num%25==0) continue;
            int maskForNum=0;
            for(int i=0;i<10;++i){
                if(num%prime[i]==0) maskForNum|=(1<<i);
            }
            //这里的u应该是从小变大,否则状态转移公式中的dp[u]没有初始化
            // int unused=1023-maskForNum;
            // for(int u=unused;u>0;u=(u-1)&unused){
            //     dp[maskForNum|u]=(dp[maskForNum|u]+cnt[num]*dp[u])%mod;
            // }
            for(int state=0;state<1024;++state){
                if((maskForNum&state)>0) continue;
                //这里可能会溢出,所以dp数组类型为long
                dp[maskForNum|state]=(dp[maskForNum|state]+cnt[num]*dp[state])%mod;
            }
        }
        //dp[0]不算进去
        for(int i=1;i<1024;++i) res=(res+dp[i])%mod;
        //有多少个1,最后的结果就乘以2的多少次方
        for(int i=0;i<cnt[1];++i) res=(res*2)%mod;
        return (int)res;
    }

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/the-number-of-good-subsets
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

posted @ 2021-09-25 19:52  归鸿唱晚  阅读(309)  评论(0编辑  收藏  举报