来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/the-number-of-good-subsets

题目描述

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

比方说,如果 nums = [1, 2, 3, 4] :
[2, 3] ,[1, 2, 3] 和 [1, 3] 是 好 子集,乘积分别为 6 = 2*3 ,6 = 2*3 和 3 = 3 。
[1, 4] 和 [4] 不是 好 子集,因为乘积分别为 4 = 2*2 和 4 = 2*2 。
请你返回 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都为任何数,所以1是在好子集中仅存在有或没有两种状态。而1-30中的除1以外的质数为2,3,5,7,11,13,17,19,23,29.如果想要成为一个好的子集,那么就需要子集中因数只有这些质数并且质数出现的次数仅为一次。那么我们可以使用一个int的低10位来记录这十个质数的是否在子集中出现。

由于样本数量十分大,但是实际上有许多重复的数据,所以可以使用计数的方法,将相同的数字使用一个vector统计出来,我这里使用了viCount,数组下标表示数字的值,数组的值代表出现的次数。

使用动态规划的方法来解决这个问题。建立一个dp[i][j] 表,i代表使用0-i之间的数构成子集,j是记录十个质数使用情况的int,状态转移式子分情况讨论:

如果i本身具有相同的质数,那么dp[i][j] = dp[i - 1][j];

如果i本身不具有相同的质数,那么dp[i][j] = dp[i -1][j] + dp[i -1][j ^ iFlag] * viCount[i] ;

其中iFlag是i所具有的质数状态,由于需要在子集中加入i,所以之前子集的元素不能出现i中的质数,即dp[i -1][j ^ iFlag]。

由于各个i中几乎不存在相关的联系,而j ^ iFlag  并且 j & iFlag肯定比 j 小,所以可以使用一维数组来压缩二维dp。

注意样本很大,所以运算后要及时取模。

代码展示

class Solution {
public:
int iNumMax = 30;
int iMod = 1000000007;
vector<int> viPrimes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
int numberOfGoodSubsets(vector<int>& nums) {
vector<int> viCount(iNumMax + 1, 0);
vector<int> dp(1 << viPrimes.size());
for(auto num:nums)
{
viCount[num]++;
} dp[0] = 1;
for(int i = 0; i < viCount[1]; i++)
{
dp[0] = dp[0] * 2 % iMod;
} for(int i = 2; i <= iNumMax; i++)
{
if(!viCount[i])
continue; int iFlags = 0;
bool bCheck = false;
for(int j = 0; j < viPrimes.size(); j++)
{
if(i % (viPrimes[j] * viPrimes[j]) == 0)
{
bCheck = true;
break;
}
if(i % viPrimes[j] == 0)
{
iFlags |= 1 << j;
}
}
if(bCheck)
continue; for(int j = (1 << viPrimes.size()) - 1; j > 0; j--)
{
if((iFlags & j) == iFlags)
{
dp[j] = (dp[j] + (long long)dp[j ^ iFlags] * viCount[i]) % iMod;
}
} }
int iRet = 0;
for(int i = 1; i < 1 << viPrimes.size(); i++)
{
iRet = (iRet + dp[i]) % iMod;
}
return iRet; }
};

运行结果

LeetCode-1994 好子集的数目的更多相关文章

  1. [Leetcode 78]求子集 Subset

    [题目] Given a set of distinct integers, nums, return all possible subsets (the power set). Note: The ...

  2. [LeetCode] Subsets II 子集合之二

    Given a collection of integers that might contain duplicates, S, return all possible subsets. Note: ...

  3. N个元素的集合划分成互斥的两个子集的数目

    前面这是寒假听马士兵老师讲的时候积累的语录.......... 1.php是水果刀,java是菜刀,刀法比较多,一年的和三年的区别很大. 2.nanicat连接mysql出现10061是服务没开启,却 ...

  4. Leetcode题目78.子集(回溯-中等)

    题目描述: 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集). 说明:解集不能包含重复的子集. 示例: 输入: nums = [1,2,3] 输出: [ [3],   [1] ...

  5. Leetcode 78题-子集

    LeetCode 78 网上已经又很多解这题的博客了,在这只是我自己的解题思路和自己的代码: 先贴上原题: 我的思路: 我做题的喜欢在本子或别处做写几个示例,以此来总结规律:下图就是我从空数组到数组长 ...

  6. leetCode 78.Subsets (子集) 解题思路和方法

    Given a set of distinct integers, nums, return all possible subsets. Note: Elements in a subset must ...

  7. 【LeetCode 90】子集 II

    题目链接 [题解] 我们在枚举下一个要取哪个数字的时候. 如 1112233 for (int i = start;i<=n;i++) //其中start-1是上一次取的位置. 如果i>s ...

  8. [Leetcode 90]求含有重复数的子集 Subset II

    [题目] Given a collection of integers that might contain duplicates, nums, return all possible subsets ...

  9. LeetCode:Subsets I II

    求集合的所有子集问题 LeetCode:Subsets Given a set of distinct integers, S, return all possible subsets. Note: ...

  10. [Leetcode 40]组合数和II Combination Sum II

    [题目] Given a collection of candidate numbers (candidates) and a target number (target), find all uni ...

随机推荐

  1. 10、比较Bigdecimal类型是否相等的方法

    一.Bigdecimal.equals()详解: Bigdecimal的equals方法不仅仅比较值的大小是否相等,首先比较的是scale(scale是bigdecimal的保留小数点位数),也就是说 ...

  2. [论文总结] kmeans聚类和WGCNA

    kmeans聚类和WGCNA 文章目录 kmeans聚类和WGCNA 论文1 论文2 论文3 总结 总结了3篇论文中kmeans聚类和WGCNA的运用. 论文1 Comprehensive disse ...

  3. Js文件名 排序

    参考了别人帖子后,调整之后的排序方法,更加精确.(参考链接在底部) 压缩版 function strCompare(str1,str2){if(str1==undefined&&str ...

  4. 这可能是Feign调用可重试的最佳方案了

    前言 在我们公司里,不同的服务之间通过Feign进行远程调用,但是,我们在尝试使调用可重试时遇到了一个小问题,Feign框架本身可以配置的自己的重试机制,但是它是一刀切的方式,所有的调用都是同样的机制 ...

  5. elasticsearch之exists查询

    一.exists查询简介 elastic search提供了exists查询,用以返回字段存在值的记录,默认情况下只有字段的值为null或者[]的时候,elasticsearch才会认为字段不存在: ...

  6. 前后端分离——使用OSS

    1. 第一步:编写OSS的工具类 点击查看代码 aliyun: oss: keyid: 填写自己的 keysecret: 填写自己的 endpoint: 填写自己的 bucketname: 填写自己的 ...

  7. SSM使用PageHelper

    第一步---->导入Maven依赖 <!--pageHelper--> <dependency> <groupId>com.github.pagehelper ...

  8. OpenMP 线程同步 Construct 实现原理以及源码分析(下)

    OpenMP 线程同步 Construct 实现原理以及源码分析(下) 前言 在上面文章当中我们主要分析了 flush, critical, master 这三个 construct 的实现原理.在本 ...

  9. IO多路复用完全解析

    上一篇文章以近乎啰嗦的方式详细描述了BIO与非阻塞IO的各种细节.如果各位还没有读过这篇文章,强烈建议先阅读一下,然后再来看本篇,因为逻辑关系是层层递进的. 1. 多路复用的诞生 非阻塞IO使用一个线 ...

  10. SQL基本概念-SQL通用语法

    SQL基本概念 1.  什么是SQL ? Structured Query Language : 结构化查询语言,其实就是定义了操作所有关系型数据库的规则.每一种数据库操作的方式存在不一样的地方,称为 ...