LeetCode刷题日记2022-2-22/动态规划&状态压缩

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

题解思路

题目给定好子集的定义以及范围【1,30】

对于任意的好子集我们添加任意数目的1 其仍为好子集

而且

  • 2,3,5,6,7,10,11,13,14,15,17,19,21,22,23,26,29,30:这些数均不包含平方因子,因此每个数在好子集中至多出现一次;
  • 4,8,9,12,16,18,20,24,25,27,28 这些数包含平方因子,因此一定不能在好子集中出现。

我们将【1,30】内的质数预先处理出来为

  • 2,3,5,7,11,13,17,19,23,29

因为每个质数只能出现一次 我们可以用一个长度为10的二进制数mask来表示这些质因数的使用情况

  • 其中mask[i]为1表示第i个质数已经使用过

定义 f[i][mask] 表示当我们只选择 [2, i]范围内的数,并且选择的数的质因数使用情况为mask 时的方案数

当我们选择的数i包含平方因子的时候 则状态转移方程为

  • f[i][mask] = f[i-1][mask]

如果i不包含平方因子的时候 状态转移方程为

  • f[i][mask] = f[i-1][mask] + f[i-1][mask\subset]*freq[i]

其中 freq[i]表示 nums中i出现的次数
[mask\subset]表示从mask中去除所有在subset中出现过的1 使用按位异或操作

题解代码

class Solution:
    def numberOfGoodSubsets(self, nums: List[int]) -> int:
        primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
        mod=10**9+7

        freq=Counter(nums)
        f=[0]*(1<<len(primes))
        f[0]=pow(2,freq[1],mod)

        for i,occ in freq.items():
            if i==1:
                continue
            subset,x=0,i

            check=True
            for j,num in enumerate(primes):
                if x%(num*num)==0:
                    check=False
                    break
                if x%num==0:
                    subset|=(1<<j)
            if not check:
                continue
            
            for mask in range((1<<len(primes))-1,0,-1):
                if (mask&subset)==subset:
                    f[mask]=(f[mask]+f[mask^subset]*occ)%mod

        ans=sum(f[1:])%mod
        return ans
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值