首页 > 其他分享 >1994. 好子集的数目

1994. 好子集的数目

时间:2022-10-31 20:57:01浏览次数:42  
标签:1994 nums 质数 30 mask 子集 数组 数目

题目描述

给一个整数的数组nums,数组中的元素不超过30,数组长度不超过\(10^5\)
给出了好子集的定义(数组中所有元素成绩可以拆分为互不相同质数的积)
对好子集个数的要求是不同子集删除的下标不同,那么也被视为不同的子集
问给定数组nums的好子集的个数?

f1-状态压缩+动态规划

基本分析

  1. 直观的看,哪些元素不能放到好子集中去?是能整除某个平方数,例如4,8,9
  2. 进一步细想,能在1-30之间,作为构建子集的质数有哪些?1+10个质数
  3. 通过以上分析,想到怎么定义dp状态?f[i][state]为考虑范围在[1,30]的前i个数,并且好子集拆解结果用到的质数为state时候的方案数
  4. 初始状态怎么定义?f[1][0]=1, 表示当只有数值1的话,只有空集是合法的
  5. 接下来怎么考虑转移?
  • 不考虑数字i,对应f[i][s] = f[i-1][s]
  • 考虑数字i,这个时候又有情况需要区分
    • 数字i可以不能分解为几个不同质数相乘,去换下一个数字i
    • 数字i可以分解成几个不同质数相乘(对应为sub)。遍历所有可以转移的前一个状态prev(prev和sub的&是0)来尝试转移
  1. 对1的情况怎么特殊处理?可以把1的次数当做系数考虑

代码

class Solution:
    def numberOfGoodSubsets(self, nums: List[int]) -> int:
        ps = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
        mod = 10**9+7
        cnt = Counter(nums)
        nmask = 1<<10
        # 定义dp状态
        # mask取0-nmask-1,i取0-30
        f = [[0]*nmask for _ in range(31)]
        # 取前1个数字,mask为0的方案个数是1
        f[1][0] = 1
        for i in range(2, 31):
            for mask in range(nmask):
                f[i][mask] = f[i-1][mask]
            if cnt[i] == 0:
                continue
            flag = True
            cur = 0
            for j, p in enumerate(ps):
                if i % (p*p) == 0:
                    flag = False
                    break
                if i % p == 0:
                    cur |= 1<<j
            if not flag:
                continue
            for prev in range(0, nmask):
                if prev & cur != 0:
                    continue
                f[i][prev|cur] = (f[i][prev|cur] + f[i-1][prev]*cnt[i]) % mod
        
        ans = 0
        for mask in range(1, nmask):
            ans = (ans + f[30][mask]) % mod
        for i in range(cnt[1]):
            ans = ans * 2 % mod
        return ans

复杂度

时间:\(数值范围为30,状态数是1024,dp部分复杂度是O(C \cdot M)\)
空间:\(O(C \cdot M)\)

总结

这个题应该没有完全吃透,后面还要回顾。
怎么把nums和质数数组对应起来?题目给了nums的取值范围是1-30,就是暗示了质数可以取其中的10个,进而哪些质数取不取就可以用二进制的mask表示。
关于枚举每个数和mask的关系,这里有些存疑,为什么要先遍历某个数i,然后遍历所有的mask?从常规定义看是考虑1-30的前i个数,且对应的质数分解为mask时的方案总数,从这个角度看也许解释的通
某个数的质数分解方面:这里的代码写的复杂了,更好的写法是试除法
赋值问题:不管这个i是不是能分解成质数,需要先把不考虑i的情况转移过来f[i][s] = f[i-1][s]
转移写法:这里是枚举之前值prev,保证prev和cur不重
边界问题:统计最终结果的时候,不能把mask=0考虑进去,但是在dp之前,必须给出f[1][0]=1的边界,这个怎么理解?
系数问题:对所有合法的结果,可以考虑cnt[1]个位置的1取或者不取都有2种可能,对应的系数是\(2^{cnt[1]}\)

标签:1994,nums,质数,30,mask,子集,数组,数目
From: https://www.cnblogs.com/zk-icewall/p/16845746.html

相关文章

  • linux 中实现将fasta文件的碱基数目转换为指定的个数
     001、每行输出为4个碱基[root@pc1test]#lstest.fa[root@pc1test]#cattest.fa>chr1aattcctt>chr2ttggaacc>chr3TTCCGG[root@pc1test]#awk'{if($0~......
  • leetcode(力扣) 78. 子集(回溯 & 巧妙解法)
    文章目录​​题目描述​​​​法一(巧妙暴力解)​​​​思路分析​​​​完整代码​​​​法二(回溯):​​​​思路分析​​​​完整代码​​题目描述给你一个整数数组nums......
  • Leetcode 90. 子集 II
    给你一个整数数组nums,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。返回的解集中,子集可以按任意顺序排列。示例1:输入:nums=[1......
  • 代码随想录day28 | 93. 复原 IP 地址 78.子集 90. 子集 II
    93.复原IP地址题目|文章思路1.先判断每个部分是不是符合要求2.如果符合要求则加入path,递归下一层3.当遍历到字符串末尾,如果有四层,则加入结果,否则直接返回。实现......
  • 数对数目
    题目给定一个长度为\(n\)的序列\(a_1,a_2,\ldots,a_n\)。请你找出一共有多少个数对\((i,j)\)满足\(a[i]\lti\lta[j]\ltj\,1\leqslanti,j\leqslantn\)......
  • 线性DP-2444. 统计定界子数组的数目
    问题描述给你一个整数数组nums和两个整数minK以及maxK。nums的定界子数组是满足下述条件的一个子数组:子数组中的最小值等于minK。子数组中的最大值等于m......
  • LeetCode2447 最大公因数等于 K 的子数组数目 题解
    看到这题,发现可以直接枚举字串进行check,复杂度是\(\mathcalO(n^2(n+\logw))\),但是可以固定左端点,向右推右端点统计答案优化到\(\mathcalO(n(n+\logw))\)。虽然这里......
  • BZOJ 4036([HAOI2015]按位或-子集和变换)
    Description刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数字,与你手上的数字进行或(c++,c的|,pascal的or)操作。选择数字i的概率是p[i]。保证0<=p[i]<=1,Σp[i]=......
  • c++算法竞赛常用板子集合(持续更新)
    前言本文主要包含算法竞赛一些常用的板子,码风可能不是太好,还请见谅。后续会继续补充没有的板子。当然我太菜了有些可能写不出来T^T稍微有些分类但不多,原谅我QwQ建议Ct......
  • Problem P33. [算法课分支限界法]分割等和子集
    动态规划,dp,即计算多加第i个数,可以达到的数值可以到多少。详细可见:https://leetcode.cn/problems/partition-equal-subset-sum/solution/fen-ge-deng-he-zi-ji-by-leetcod......