2022-02-13 每日打卡:难题精刷

2022-02-13 每日打卡:难题精刷

写在前面

“这些事儿在熟练之后,也许就像喝口水一样平淡,但却能给初学者带来巨大的快乐,我一直觉得,能否始终保持如初学者般的热情、专注,决定了在做某件事时能走多远,能做多好。” 该系列文章由python编写,所刷题目共三个来源:之前没做出来的 ;Leetcode中等,困难难度题目; 周赛题目;某个专题的经典题目,所有代码已AC。每日1-3道,随缘剖析,希望风雨无阻,作为勉励自己坚持刷题的记录。

204. 计数质数

在这里插入图片描述
枚举不再给出,最大问题在于没有考虑到数与数的关联性。

这里先学习一下【埃及筛(厄拉多塞筛法)】:

  • 考虑x是质数,则其倍数2x,3x…一定不是质数。
  • 可以直接从 x⋅x 开始标记,因为 2x,3x… 这些数一定在 x 之前就被其他数的倍数标记过了,例如 2 的所有倍数,3 的所有倍数等。
class Solution:
    def countPrimes(self, n: int) -> int:
        # 从2开始检查,检查到n-1(小于等于就检查到n了)
        isprime = [1 for i in range(0,n)]
        if len(isprime)<=1:
            return 0
        isprime[0]=isprime[1]=0
        for i in range(2,n):
            if isprime[i]:
                for j in range(i*i,n,i):
                    isprime[j]=0
        return sum(isprime)
        

【线性筛】的做法更加高级,为了消除埃及筛中的冗余操作:

  • 比如45会被3和5两个数同时标记,我们期望 O ( n ) O(n) O(n) 的复杂度,也就是每个数只被判定一次。根据《算术基本定理》:【每一个合数都可以以唯一形式被写成质数的乘积】。换言之,多个质数的乘积只能组成唯一的合数
  • 不再标记 x 的所有倍数,而只标记质数集合中的数与 x 相乘的数。
  • 对于多个质数相乘的问题,不仅仅对质数进行合数标记,而是对每个数都进行,譬如 8 = 4 ∗ 2 8=4*2 8=42 中4是不能被忽略的。
  • 每次标记时标记至 x m o d    p r i m e s i = = 0 x \mod primes_i==0 xmodprimesi==0,因为如果x可以整除某个质数,那么对于合数 x ∗ p r i m e s i = x / p r i m e s i ∗ p r i m e s i + 1 x * primes_i = x/primes_i * primes_{i+1} xprimesi=x/primesiprimesi+1,即后面一定存在一个更大的数使得其被标记。另一个博主的介绍中说目的是【用最小的质因子把他筛掉】。
class Solution:
    def countPrimes(self, n: int) -> int:
        # 从2开始检查,检查到n-1(小于等于就检查到n了)
        isprime = [1 for i in range(0, n)]
        if len(isprime) <= 1:
            return 0
        isprime[0] = isprime[1] = 0
        primes = []
        for i in range(2, n):
            if isprime[i]:
                primes.append(i)
            for j in primes:
                if j*i < n :
                    isprime[i*j] = 0
                else:
                    break
                # 必须先加入再退出
                # 4 = 2*2
                if i % j == 0:
                    break
        return sum(isprime)
        

2172. 数组的最大与和

在这里插入图片描述

  • 动态规划+状态压缩:从小到大使用 进制状态 来表明当前枚举到第几个数字,这样可以不用枚举 nums 数组,从降低一个 O ( N ) O(N) O(N) 的复杂度。而把一放二变成一放一的,可以直接通过统计1的个数来确定放到了第几个数。
class Solution:
    def maximumANDSum(self, nums: List[int], numSlots: int) -> int:
    	# 状态压缩篮子为 numSlots * 2 位的二进制数
    	# 这样每个篮子只能放一个
        f = [0] * (1 << (numSlots * 2))
       	# 每个i代表的就是 从0到1<<(numSlots * 2)
       	# 也就是篮子放数的所有可能性
        for i, fi in enumerate(f):
			# 有几个1说明已经放了几个数字
            c = i.bit_count()
            # 超过len(nums)个篮子放了数,已经没数可放,过
            if c >= len(nums):
                continue
            for j in range(numSlots * 2):
            	# 枚举空篮子 j
                if (i & (1 << j)) == 0:
                	# 空蓝子j放上1,状态改变为s  
                    s = i | (1 << j)
                    f[s] = max(f[s], fi + ((j // 2 + 1) & nums[c]))
        return max(f)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值