zoukankan      html  css  js  c++  java
  • 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
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 相关阅读:
    自动化部署之jenkins及简介
    gitlab的备份与恢复与迁移
    P2561 [AHOI2002]黑白瓷砖
    P2042 [NOI2005]维护数列
    P2156 [SDOI2009]细胞探索
    P2154 [SDOI2009]虔诚的墓主人
    P2148 [SDOI2009]E&D
    2019.2.26考试T2 矩阵快速幂加速DP
    loj #6485. LJJ 学二项式定理 (模板qwq)
    P3224 [HNOI2012]永无乡
  • 原文地址:https://www.cnblogs.com/Frank-Hong/p/15335301.html
Copyright © 2011-2022 走看看