ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【leetcode刷题第40天】1994.好子集的数目

2022-02-22 22:03:40  阅读:182  来源: 互联网

标签:subset 1994 nums int 40 子集 leetcode dp MOD


第四十天

1994 好子集的数目

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

  • 比方说,如果 nums = [1, 2, 3, 4]
    • [2, 3][1, 2, 3][1, 3] 子集,乘积分别 为 6 = 2*36 = 2*33 = 3
    • [1, 4][4] 不是 子集,因为乘积分别为 4 = 2*24 = 2*2

请你返回 nums 中不同的 子集的数目对 10^9 + 7 取余 的结果。

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

方法

使用状态压缩dp来处理这个问题,首先我们得明确以下几点:

  • 如果一个子集是好子集,那么我们对其中的每一个元素进行质因数分解,得到的最终结果就是 题目中要求的互不相同的质数的乘积。换言之,对于4,9..等数,他们的质因数分解存在两个或以上相同的质因子,则不可能存在于好子集中。
  • 对于1,其起到的作用可有可无,因为1和任何数相乘都不会发生变化,所以对于所有的1,其可取可不取,它的存在会使最终的结果翻 2 c o u n t [ 1 ] 2^{count[1]} 2count[1]倍。
  • 30以内的质数只有2,3,5,7,11,13,17,19,23,2910个,最极端的情况是这10个数都出现一次,对于其他情况,我们对好子集进行质因数分解,只要上述各个质数出现的次数不多于1次,就是一个合法的情况。我们使用一个长度为10的二进制数来表示各个质数出现的情况,对于状态status,其第i位上的1表示第i个质数出现了一次。

我们定义dp[i]表示以i作为状态码的好子集的数量,例如dp[1024]1024的二进制表示是1111111111,表示上述10个质数都出现了一次,它们所组成的乘积对应的好子集的数量。

考虑状态转移方程,对于一个数a,我们先预先统计出其出现的次数,然后将其转换为不同的质因数的积,将这些不同的质因数转换成一个subset,表示能够用这个数a能够引出来的状态。然后我们从a这个状态去转移,当一个status & subset = 0时,表明这个status能够由subset这个状态转移过来。

class Solution {
    public static final int MOD = 1000000007;
    public int numberOfGoodSubsets(int[] nums) {
        int[] primes = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
        int[] quantityOfNumberI = new int[31];
        for (int i : nums) quantityOfNumberI[i]++;
        long[] dp = new long[(1 << primes.length) + 1];
        dp[0] = 1;
        for (int i = 2; i <= 30; ++i) {
            if (quantityOfNumberI[i] == 0) continue;
            int subset = 0;
            boolean isValid = true;
            for (int j = 0; j < primes.length; ++j) {
                if (i % (primes[j] * primes[j]) == 0) {
                    isValid = false;
                    break;
                }
                if (i % primes[j] == 0) subset |= (1 << j);
            }
            if (isValid) {
                for (int mask = 1; mask <= (1 << primes.length); ++mask) {
                    if ((mask & subset) == subset)
                        dp[mask] = (dp[mask] % MOD + ((dp[mask ^ subset] % MOD) * quantityOfNumberI[i]) % MOD) % MOD;
                }
            }
        }
        long res = 0;
        for (int i = 1; i < dp.length; ++i) res = (res % MOD + dp[i] % MOD) % MOD;
        for (int i = 0; i < quantityOfNumberI[1]; ++i) res = (res * 2) % MOD;
        return (int) res;
    }
}

标签:subset,1994,nums,int,40,子集,leetcode,dp,MOD
来源: https://blog.csdn.net/tomly2020/article/details/123078478

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有