前言
首先,说一下我这场周赛的情况,只出来两道题,太久没打周赛了,越来越没有题感了,今后得继续努力了!下面我们进入正题!
100031. 计算 K 置位下标对应元素的和
思路讲解
方法一:直接调用库函数,求解每个元素下标的二进制中1的个数!
class Solution:
def sumIndicesWithKSetBits(self, nums: List[int], k: int) -> int:
ans = 0
for i,x in enumerate(nums):
if i.bit_count() == k:
ans += x
return ans
方法二:使用lowbit,手动写求解二进制中1个数的函数!
class Solution:
def sumIndicesWithKSetBits(self, nums: List[int], k: int) -> int:
def f(x):
cnt = 0
while x:
# x & -x ——> lowbit的操作!
x -= (x & -x)
cnt += 1
return cnt
ans = 0
for i,x in enumerate(nums):
if f(i) == k:
ans += x
return ans
100040. 让所有学生保持开心的分组方法数
思路讲解
如果能够满足下述两个条件之一,则认为第 i 位学生将会保持开心:
①这位学生被选中,并且被选中的学生人数 严格大于 nums[i] 。
②这位学生没有被选中,并且被选中的学生人数 严格小于 nums[i] 。
这种答案与选择顺序无关的题目,我们一般会将其排序,然后寻找规律!【做题经验】
我们选择一个示例:
考虑两种特殊情况:
①一个也不选:
选中的学生人数为0,那么,min(nums) = nums[0] > 0;
对于示例而言很明显是不满足的,因为nums[0] = 0。
②所有的都选了:
选中的学生人数为n,那么,max(nums) = nums[n - 1] < n;
对于示例而言是满足的。
考虑一般情况:
如果选了nums[i],是否一定要选nums[i - 1]呢?
这是一定的,为什么呢?
我们设选了nums[i]后,被选中的学生人数为 x。
根据题意,nums[i] < x, nums[i - 1] > x;
但nums[i - 1] <= nums[i],这很明显是矛盾的,于是,反证完成!
假设,我们选了nums[i],那么说明,我们选了 i + 1的学生,于是有 nums[i] < i + 1;
同时,又要满足,i + 1 < nums[i + 1];
综上,得出 nums[i] < i + 1 < nums[i + 1]!
# 比赛时写的
class Solution:
def countWays(self, nums: List[int]) -> int:
nums.sort()
n = len(nums)
ans = 0
if nums[0] != 0:
ans = 1
for cnt in range(1,n + 1):
if nums[cnt - 1] < cnt:
if cnt != n:
if nums[cnt] > cnt:
ans += 1
else:
ans += 1
return ans
# 比完赛,看我偶像灵神写的!
class Solution:
def countWays(self, nums: List[int]) -> int:
nums.sort()
n,ans = len(nums),nums[0] != 0
for i in range(n - 1):
if nums[i] < i + 1 < nums[i + 1]:
ans += 1
return ans + 1
100033. 最大合金数
思路讲解
这个题要注意这样一句话:所有合金都需要由同一台机器制造。 不然就寄了!
思路就很简单,枚举k台机器,看哪一种制造的合金多就行!由于,题目中限制了budget,那么我们就可以来一波二分操作,二分制造的合金数,求出最多的合金的数!
注意二分的边界:low与high,最少肯定是0,那么最多为多少呢?
这里我讲一下灵神的思路:
假设,cost全部设置为1,且每种材料需要一种一种金属,那么至少可以制造budget // n块合金,假设stock的存储量均为最大值max(stock),那么又可以制造 max(stock)块合金,于是就可求出假设的最多合金数: max(stock) + budget // n
那么,我们该运用哪种二分模板呢?
首先,制造合金越多,其花的钱自然也越多,如何在指定预算下,求出最多的合金数量呢?
其实,就等价于: 11111110000000,求满足条件区间的右端点!
就可以用模板:
# 二分指定区间的右端点
while left < right:
mid = left + right + 1 >> 1
if check(mid):
left = mid
else:
right = mid - 1
class Solution:
def maxNumberOfAlloys(self, n: int, k: int, budget: int, composition: List[List[int]], stock: List[int], cost: List[int]) -> int:
low,high = 0,max(stock) + budget // n
ans = 0
def check(i):
money = 0
for num,s,c in zip(com,stock,cost):
if s < num * i:
money += (num * i - s) * c
if money > budget:
return 0
return 1
for com in composition:
left,right = low,high
while left < right:
mid = left + right + 1 >> 1
if check(mid):
left = mid
else:
right = mid - 1
ans = max(ans,left)
return ans
这里也贴一下,求满足区间左端点的代码。
while left < right:
mid = left + right >> 1
if check(mid):
right = mid
else:
left = mid + 1
8041. 完全子集的最大元素和
思路讲解:
此题考查的是数学思维,我们先来讲解一下,如何才能满足一对元素的乘积是完全平方数?
那么,我们是否可以这样认为?
如果两个数,除去为个数为偶数个的因数后的数相同,那么其结果一定为完全平方数?
答案是肯定的!
证明:
令
x = a * a * a * a * b * c
y = u * u * v * v * b * c
x * y = (a * a * a * a * b * c ) * (u * u * v * v *b * c) = (a * a * u * v * b * c) * (a * a * u *v * b c)
令 z = a * a * u * v * b c,那么 x * y = z * z,为完全平方数!
那么,问题的关键就在于如何定义一个函数 f,使得 f(x) 为去除个数为偶数的质因数后的数!
其实,只要在分解质因数的代码中,添加一个判断就行了!
具体如下:
def f(x):
i = 2
ans = 1
while x // i >= i:
cnt = 0
while x % i == 0:
cnt += 1
x //= i
if cnt & 1:
ans *= i
i += 1
if x > 0:
ans *= x
return ans
class Solution:
def maximumSum(self, nums: List[int]) -> int:
n = len(nums)
cnt = [0 for _ in range(n + 1)]
for i,x in enumerate(nums):
cnt[f(i + 1)] += x
return max(cnt)
这个的时间复杂度为 O(n * sqrt(n))
同时,灵神还介绍了另一种O(n)的做法!
class Solution:
def maximumSum(self, nums: List[int]) -> int:
n = len(nums)
ans = 0
for i in range(1,n + 1):
tmp = 0
for j in range(1,isqrt(n // i) + 1):
tmp += nums[i * j * j - 1]
ans = max(ans,tmp)
return ans
这个代码,将每一个数x,表达为x = i * (j * j)的形式,其中i就是我们上面所说的f(x)。
这样做更简洁,更优雅,更美丽,更快捷!
总结
以上,便是每次周赛的全部内容啦!
虽然写这篇博客花费了许多时间,但还是学到了许多东西,之前,我一直认为写博客浪费时间,一直没有好好总结,导致我现在什么题也不会做了,之前会做的题也都忘了,现在回头还要再学,这样其实浪费的时间更多。写博客的成效,时间会向我证明的,希望之后能慢慢进步吧!大学的时光不多了,今后也要好好加油!