其他分享
首页 > 其他分享> > 【leetcode刷题第40天】1994.好子集的数目

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

作者:互联网

第四十天

1994 好子集的数目

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

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

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

方法

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

我们定义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