题目
给你一个整数数组 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。