力扣第363场周赛

前言

首先,说一下我这场周赛的情况,只出来两道题,太久没打周赛了,越来越没有题感了,今后得继续努力了!下面我们进入正题!

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)。

这样做更简洁,更优雅,更美丽,更快捷!

总结

以上,便是每次周赛的全部内容啦!
虽然写这篇博客花费了许多时间,但还是学到了许多东西,之前,我一直认为写博客浪费时间,一直没有好好总结,导致我现在什么题也不会做了,之前会做的题也都忘了,现在回头还要再学,这样其实浪费的时间更多。写博客的成效,时间会向我证明的,希望之后能慢慢进步吧!大学的时光不多了,今后也要好好加油!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值