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=4∗2 中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} x∗primesi=x/primesi∗primesi+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)