【小羊】第 288 场力扣周赛(Python版)题解
1318
2022.04.10
2022.04.11
发布于 未知归属地

突然想之后每次参加的小竞赛都写一份题解,这样可能以后查看啥的也方便,并进一步优化比赛时代码。那么话不多说我们开始!

第一题:按奇偶性交换后的最大整数

这题如果能够记录奇数和偶数对应的位置情况,再对奇数和偶数分别进行降序排列并填入相应的位置,可以得到最大的整数(由于最大数位的位置只可能变大或不变,而不可能变小,因此结果一定合法)。
具体而言 先记住奇偶数分别的位置,即 位置为奇数, 位置为偶数;再对对应数进行排序,奇数排序结果为 ,偶数排序结果为 。因此很容易依次在对应位置填数可以得到结果为
具体处理,Python 转为字符串中每一项的判断将比较容易,并且位置的记录也不容易出错。
当然,此题如果要变为交换后的最小整数,则需要考虑结果的合法性,即不能以 作为一整数的开头,需在排序后进行一定的判断,可能能小小提高难度。

第一题的代码

class Solution:
    def largestInteger(self, num: int) -> int:
        evens = [i for i in str(num) if int(i) % 2 == 0]
        odds = [i for i in str(num) if int(i) % 2 == 1]
        # 记录不同奇偶性数码分别有哪些数
        evens.sort(reverse=True)
        odds.sort(reverse=True)
        pos = {x for x, val in enumerate(str(num)) if int(val) % 2}
        # 查找对应位置是奇数的位置
        result = ''
        pos_e = 0
        pos_o = 0
        # 分别记录此时两数码分别取到了何位置的数
        for i in range(len(str(num))):
            if i in pos:
                result += odds[pos_o]
                pos_o += 1
            else:
                result += evens[pos_e]
                pos_e += 1
        return int(result)

第二题:向表达式添加括号后的最小结果

这题由于 Python 有可以直接计算表达式值的 eval 函数,大大简化了程序计算的难度(但是还是傻傻的以为要输出最小结果而非最小表达式被罚时一次,实在是太亏了),整体而言只有括号在最开头和最结尾的时候会不需要在两侧添加 符号,因此程序主要注意的也是这一点,最后也需要将添加的 符号删去;另外加括号处不能为前一数的末尾以及后一数的开头,也需要在循环时加以判断。
整体思路在于生成所有可能表达式,并计算值,若比目前最小情况更小,则更新对应最小情况。这题用 Python 还是非常轻松愉快的。至于复杂度,完全取决于前后两数长度的乘积,以及 eval 表达式的计算。

第二题的代码

class Solution:
    def minimizeResult(self, expression: str) -> str:
        num1, num2 = expression.split('+')
        result = eval(expression)
        ans = f'({expression})' 
        len1, len2 = len(num1), len(num2)
        for i in range(len1):
            for j in range(1, len2+1):
                expr = num1[:i] + '*' *(num1[:i]!='') + '(' + num1[i:] + '+' + num2[:j] + ')' + '*'*(num2[j:] != '') +  num2[j:]
                if eval(expr) < result:
                    result = eval(expr)
                    ans = expr.replace('*','')
        return ans

第三题:K 次增加后的最大乘积

仿佛刚刚进入一个比较有意思的题,关键在于每次操作的数为最小的数。(这一点的说明好像比较复杂,可以理解为和一定的若干个数越接近乘积越大)因此不少解答利用了堆进行每一次相加的模拟,但这样做最终复杂度会与 的大小有关,因而存在优化空间。
我们这里采取二分的方式解决对应的问题,即我们考虑,可以通过 次操作把前面多少个数变成相同的数。为了达到这样的目的,我们先对数字进行排序;对每个点如果能通过 次操作把前面 个数变为相同,则需要满足条件,最大值的 倍小于前 个数的和加上 。得到这样的二分条件后,我们可只对前面这 个数进行操作,使得其极差不超过 (可通过部分数取均值,其他取均值加一得到);剩余数直接乘积。注意:每次乘积都要取模,避免数值过大计算复杂度过大。

第三题的代码

class Solution:
    def maximumProduct(self, nums: List[int], k: int) -> int:
        nums.sort()
        acc_sum = [0]
        acc = 0
        for num in nums:
            acc += num
            acc_sum.append(acc)
        l, r = 0, len(nums)-1
        while l <= r:
            m = (l+r) // 2
            # 这里是前面提到的判断
            if -acc_sum[m+1] + nums[m]*(m+1) <= k: l = m+1
            else: r=m-1
        ans = 1
        # 后面的数可以直接不操作
        for i in range(l, len(nums)):
            ans *= nums[i]
            ans %= 10**9+7
        acc = acc_sum[r+1]+k
        # 通过剩余数的和进行类似平均分配,bigger_num表示均值+1的数的个数
        bigger_num = acc % (r+1)
        tmp = acc //(r+1)
        for i in range(bigger_num):
            ans *= tmp+1
            ans %= 10**9+7
        for i in range(r+1-num1):
            ans *= tmp
            ans %= 10**9+7
        return ans

第四题:花园的最大总美丽值

这题跟前一题极其相关!因为都要控制一部分量满足最小值尽可能大的特性(这也是我认为这次周赛出题可能不够好的地方)。我们先顺着一个容易犯错的方法进行考虑(这样这题才显得更困难)。
我们考虑先将花园的数组排序(这么做的理由类似于上一题,可以直接截取到一点看前面的未 full 的花最多可以每一堆最少到多少朵),因此只要遍历对应的 full 的堆数,确定其前的最大的最小值是多少即可。
接下来是需要修正的一些细节:
最小值不能超过 full 的数量,因此要取其和 full-1 的最小值;
最重要的一点——最小值一定能取到 full-1 吗?
不一定!因为前面可能已经取到了full!!
所以开始不需要进行花朵添加的情况下,可以尽量往前作为遍历的起点;
这里可以不用二分查找进行处理,因为随着 full 组数量增加,前面最大的最小值保持单调变换,可以直接往前走进行对比,后续整体时间复杂度可以减小到 (但这里暂未做对应优化)。虽然整体的复杂度由于开始的排序仍然为

第四题的代码

class Solution:
    def maximumBeauty(self, flowers: List[int], newFlowers: int, target: int, full: int, partial: int) -> int:
        flowers.sort()
        # 分别计算从任一点往前的总花数和往后要全部填满的总花数
        acc_sum = []
        acc = 0
        for flower in flowers:
            acc += flower
            acc_sum.append(acc)
        lack = []
        acc = 0
        for flower in flowers[::-1]:
            acc+=max(0,target-flower)
            lack.append(acc)
        lack = lack[::-1]
        lack.append(0)
        # 进行得分的计算
        ans = 0
        for i in range(len(flowers), -1, -1):
            # 如果新的一项不需要任何花数,则持续向前,解决了前面的重点问题
            if i and lack[i] == lack[i-1]: continue
            # 对剩余的花进行分配,若没有剩余花,则结束讨论!
            res = newFlowers - lack[i]
            if res < 0: break
            l, r = 0, i-1
            while l <= r:
                m = (l+r)//2
                if flowers[m] *(m+1)-acc_sum[m] > res: r = m-1
                else:l=m+1
            partial_flower = min((acc_sum[r] + res)//max(1,r+1), target-1)
            # i!=0 的作用?不存在未满的情况下
            ans = max(ans, full*(len(flowers)-i)+partial_flower*partial * (i!=0))
        return ans
评论 (3)