leetcode 1994. 好子集的数目 思维dfs或递推或二进制枚举

leetcode 1994. 好子集的数目 dfs

给你一个整数数组 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

题解

解法1:dfs

nums[i] <= 30,其中数的因子为平方数的肯定不行,即可以排除4, 8, 9,12,16,18 ,20,24,25, 27,28,我们对剩下的19个且存在的数dfs求方案即可。

带1的情况特殊,2^count[1] * res 即可((选1或者不选1)

解法2: 二进制枚举(状压)

基本跟dfs一个思路,看一下代码即可,但T了,需要优先处理出来合法状态,当我太懒了。

解法3:递推

遍历可能作为好子集元素的数,用 哈希表 d 维护能得到的乘积和对应的好子集个数。 可以递推。

const int N = 31, MOD = 1e9 + 7;
typedef long long LL;
class Solution {
public:
    int count[N] = {0},  C = 1;
    vector<int> path;
    bool st[N] = {false}, g[N][N] = {false};
    int gcd(int a, int b){
        return b ? gcd(b, a % b) : a;
    }
    // u 当前枚举的数;sum 当前方案数
    int dfs(int u, int sum){
        if(!sum) return 0; //剪枝,0*x=0
        if(u >= N){
            if(path.empty()) return 0;
            return sum * (LL)C % MOD;
        }
        // 不选u
        int res = dfs(u + 1, sum);
        // 选u
        if(!st[u]){
            bool flag = true;
            for(int x : path){
                if(g[x][u]){
                    flag = false;
                    break;
                }
            }
            if(flag){
                path.push_back(u);
                res = (res + dfs(u + 1, sum * (LL)count[u] % MOD)) % MOD;
                path.pop_back();
            }
        }

        return res;
    }
    int numberOfGoodSubsets(vector<int>& nums) {
        for(int x : nums) count[x] ++;

        //筛掉不能选的数字, 所有包含平方因子的数标记为 true
        for(int i = 2; i * i < N; i++){
            for(int j = 1; j * i * i < N; j++){
                st[j * i * i] = true;
            }
        }

        //将不互质互质的数连边
        for(int i = 1; i < N; i++){
            for(int j = 1; j < N; j++){
                if(gcd(i, j) > 1)
                   g[i][j] = true;
            }
        }
        for(int i = 0; i < count[1]; i++) C = (C * 2) % MOD;
        return dfs(2, 1);
    }
};
const int N = 31, MOD = 1e9 + 7;
typedef long long LL;
class Solution {
public:
    int count[N] = {0},  C = 1;
    bool st[N] = {false}, g[N][N];
    vector<int> all = {2,3,5,6,7,10,11,13,14,15,17,19,21,22,23,26,29,30};
    
    int numberOfGoodSubsets(vector<int>& nums) {
        for(int x : nums) count[x] ++;

        //筛掉不能选的数字, 所有包含平方因子的数标记为 true
        for(int i = 2; i * i < N; i++){
            for(int j = 1; j * i * i < N; j++){
                st[j * i * i] = true;
            }
        }
        //将不互质的数连边
        for(int i = 1; i < N; i++){
            for(int j = 1; j < N; j++){
                if(gcd(i, j) > 1)
                   g[i][j] = true;
            }
        }
        LL res = 0;
        int n = 18;
        for(int i = 1; i < 1<<n; i++){
            int t = 1;
            bool flag = false;
            vector<int> v;
            //cout<<bitset<18>(i)<<" ";
            for(int j = 0 ;j < n; j++){
                if(i >> j & 1){
                    // 如果有一个是0,0 * x == 0
                    if(!count[all[j]]){
                        flag = true;
                        break;
                    }
                    // 判断前面是否出现过互质的数
                    for(int x : v){
                        if(g[x][all[j]]){
                            flag = true;
                            break;
                        }
                    }
                    if(flag) break;
                    t = (LL)t * count[all[j]] % MOD;   
                    v.push_back(all[j]);
                }
                if(flag) break;
            }
            cout<<t<<endl;
            if(!flag) res = (res + t) % MOD;
        }
        
        for(int i = 0; i < count[1]; i++) C = (C * 2) % MOD;
        return res * C % MOD;
    }
};

遍历可能作为好子集元素的数,用 哈希表 d 维护能得到的乘积和对应的好子集个数。 可以递推。

def numberOfGoodSubsets(self, nums: List[int]) -> int:
    ct, mod = Counter(nums), 10**9+7
    d = defaultdict(int)
    d[1] = (1 << ct[1]) % mod
    for num in [2, 3, 5, 6, 7, 10, 11, 13, 14, 15, 17, 19, 21, 22, 23, 26, 29, 30]:
        for x in list(d):
            if math.gcd(num, x) == 1:
                d[num*x] += ct[num]*d[x]
                d[num*x] %= mod
    return (sum(d.values())-d[1]) % mod
posted @ 2021-09-07 15:37  pxlsdz  阅读(107)  评论(0编辑  收藏  举报