Python解题 - CSDN周赛第43期

Posted 请叫我问哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python解题 - CSDN周赛第43期相关的知识,希望对你有一定的参考价值。

感觉周赛越来越无趣了,基本都是考过的题目。上期周赛也是,4道题都曾考过,问哥也都写过题解,奖品也不吸引人,实在没什么好写了。

回想前段时间用力过猛,刷了C站大部分OJ题,以致于现在看到题目就直接套答案了。甚至于误打误撞,发现了周赛的测试链接,上了一次处罚名单。。。嗯(无辜脸)。。。

本期还是全部考过,除了第二题没写过题解,这里就把它补上吧。


第一题:判断胜负

已知两个字符串A,B。连续进行读入n次。每次读入的字符串都为A|B。输出读入次数最多的字符串。

分析

17期考过,可以参考问哥之前的题解。不过其实也没啥好解的,就是比较两个字符串哪个出现次数最多,平局就输出“dogfall”——顺便学个英文单词。


第二题:小豚鼠搬家

小豚鼠排排坐。 小艺酱买了一排排格子的小房子n*m,她想让k只小豚鼠每只小豚鼠都有自己的房子。 但是为了不浪费空间,她想要小房子的最外圈尽量每行每列都有一只小豚鼠居住(小豚鼠也可以住在中间的格子,只需保证房子最外围的行和列至少住一只豚鼠即可,无需每行每列都有豚鼠)。 小艺酱想知道自己有多少种方案安排小豚鼠。

输入描述:输入整数n,m,k。(1<=n,m<=20,0<=k<=n*m)

输出描述:输出方案数,答案对1e9+7取模。

示例:

输入3 3 2
输出2

分析

也曾在13期考过,但是网上靠谱的题解不多。

本题本质上就是排列组合,总共有  个格子,要放进去  只小豚鼠,如果不加任何限制的话,学过数学的都知道,总共有  种方案。但是题目要求最外面一圈的每行每列(实际上就是顶底两行与左右边两列)至少各有一只小豚鼠。在检查这个条件时,小豚鼠的数量可以重复计入。比如给的例子中,只有两只小豚鼠,于是只能这样放才能满足要求(也就是两种方案):

明白了题目的要求,我们可以先思考一下最朴素的穷举做法:先找出所有  种可能,然后逐一检查每种可能里,上、下、左、右四条边里有没有豚鼠。

这很容易,只要使用 python 内置的 combinations 函数,找出所有从坐标  到  的所有坐标中取出  个的组合,然后逐一检查其中是否存在横坐标为  和 ,以及纵坐标为  和  的坐标。

这种方法可以保证找到答案,但绝对会超时。因为我们其实并不关心每种方案的具体坐标有哪些,而只需要知道方案的数量。于是,我们拿总的方案数,减去不符合要求的方案不就可以了?

我们已知总的方案数是 ,那么不符合要求的有哪些方案呢?

  1. 一条边没有豚鼠:
    1. 顶边或底边没有豚鼠:
    2. 左边或右边没有豚鼠: 
  2. 两条边没有豚鼠:
    1. 顶边和底边没有豚鼠:
    2. 左边和右边没有豚鼠:
    3. 顶边和左边、顶边和右边、底边和左边、底边和右边没有豚鼠:
  3. 三条边没有豚鼠:
    1. 顶边、左边、底边,或顶边、右边、底边没有豚鼠:
    2. 左边、顶边、右边,或左边、底边、右边没有豚鼠:
  4. 四条边没有豚鼠: 

但是别急,当我们拿总数减去四次一条边没有豚鼠的情况的时候,实际上,我们重复减去了那些两条边、三条边的情况,所以我们要把两条边的情况加回来,但是这样一来,又重复加上了三条边的情况,所以还要将三条边的情况减去。。。所以,相信你也已经看出来了,实际上这题考察的又是容斥原理

如果我们用  代表总的方案的集合, 各自代表上、下、左、右四条边上没有豚鼠的情况的集合,可以用数学公式表示如下:

一言以蔽之:将四种集合进行排列组合,如果组合中包含的集合个数是奇数,就减去,如果是偶数,就加上。

所以,回到我们这道题,结合我们刚才列出的所有情况,就可以得到答案如下:

代码就省略了吧,无非是用代码计算这个公式罢了。 


第三题:醉酒的狱卒

某监狱有一个由n个牢房组成的大厅,每个牢房紧挨着。每个牢房里都有一个囚犯,每个牢房都是锁着的。一天晚上,狱卒感到无聊,决定玩一个游戏。在第一轮,他喝了一杯威士忌,然后跑下大厅,打开每个牢房的锁。在第二轮比赛中,他喝了一杯威士忌,然后跑下大厅,锁上每隔一个的牢房的锁(牢房2、4、6…)。在第三轮比赛中,他喝了一杯威士忌,然后跑下大厅。他每隔三个牢房(第3、6、9号牢房)就去一次。如果牢房被锁上了,他就把它打开;如果牢房门打开了,他就锁上牢房。他重复 n 轮,喝最后一杯,然后昏倒。一些囚犯(可能为零号)意识到他们的牢房被解锁且狱卒丧失了行动能力。他们就可以立即逃跑。现在根据牢房数量,确定有多少囚犯越狱。

分析 

19期考过,题干很复杂,实际很简单。本题存在  的解法,可以参考问哥之前的题解


第四题:会议安排

开会了!作为一个集体,开会的时候桌子当然是需要和老大相邻的!(老大可能坐在桌子上边) 小艺被分配到排桌椅的活,可是小艺的力气都用在吃上了,怎么可能搬动这些桌椅呢。 她决定用现有的布局当作是会议座位安排。每个桌子分配一个人。互相连接相同字符表示一个桌子,如UVV表示2张桌子,UVU则表示3张桌子。小艺想知道选择某个桌子之后老大身边能围多少人?

分析

20期考过,可以参考问哥之前写的题解

关于字符串的处理,问哥一直觉得很麻烦,所以如果有更好的做法,还请告诉我,谢谢。

LeetCode周赛第193场周赛

一、5436. 一维数组的动态和

给你一个数组 nums 。数组「动态和」的计算公式为:runningSum[i] = sum(nums[0]…nums[i])

请返回 nums 的动态和。

示例:

输入:nums = [1,2,3,4]
输出:[1,3,6,10]
解释:动态和计算过程为 [1, 1+2, 1+2+3, 1+2+3+4] 。

输入:nums = [1,1,1,1,1]
输出:[1,2,3,4,5]
解释:动态和计算过程为 [1, 1+1, 1+1+1, 1+1+1+1, 1+1+1+1+1] 。

分析:

? 一遍遍历即可。

代码(Python):

class Solution:
    def runningSum(self, nums: List[int]) -> List[int]:
        pre = 0
        ans = []
        for val in nums:
            pre += val
            ans.append(pre)
        return ans

二、5437. 不同整数的最少数目

给你一个整数数组 arr 和一个整数 k 。现需要从数组中恰好移除 k 个元素,请找出移除后数组中不同整数的最少数目。

示例:

输入:arr = [5,5,4], k = 1
输出:1
解释:移除 1 个 4 ,数组中只剩下 5 一种整数。

输入:arr = [4,3,1,1,3,3,2], k = 3
输出:2
解释:先移除 4、2 ,然后再移除两个 1 中的任意 1 个或者三个 3 中的任意 1 个,最后剩下 1 和 3 两种整数。

分析:

? (1)首先需要对数组中元素进行计数,Python提供了基于哈希表实现的Counter。

? (2)接着就是排序加遍历,个数少的数优先-1,当个数为0时去除。

代码(Python):

class Solution:
    def findLeastNumOfUniqueInts(self, arr: List[int], k: int) -> int:
        counter = Counter(arr)
        res = []
        for key in counter:
            res.append([key, counter[key]])
        
        res.sort(key=lambda x: -x[1])
        while k > 0:
            res[-1][1] -= 1
            k -= 1
            if res[-1][1] == 0:
                res.pop()
        return len(res)

三、5438. 制作 m 束花所需的最少天数

给你一个整数数组 bloomDay,以及两个整数 m 和 k 。

现需要制作 m 束花。制作花束时,需要使用花园中 相邻的 k 朵花 。

花园中有 n 朵花,第 i 朵花会在 bloomDay[i] 时盛开,恰好 可以用于 一束 花中。

请你返回从花园中摘 m 束花需要等待的最少的天数。如果不能摘到 m 束花则返回 -1 。

示例:

输入:bloomDay = [1,10,3,10,2], m = 3, k = 1
输出:3
解释:让我们一起观察这三天的花开过程,x 表示花开,而 _ 表示花还未开。
现在需要制作 3 束花,每束只需要 1 朵。
1 天后:[x, _, _, _, _] // 只能制作 1 束花
2 天后:[x, _, _, _, x] // 只能制作 2 束花
3 天后:[x, _, x, _, x] // 可以制作 3 束花,答案为 3

输入:bloomDay = [1,10,3,10,2], m = 3, k = 2
输出:-1
解释:要制作 3 束花,每束需要 2 朵花,也就是一共需要 6 朵花。而花园中只有 5 朵花,无法满足制作要求,返回 -1 。

分析:

? (1)去重、排序:

? 由于题目给出的bloomDay数组中可能含有重复元素。并且题目需要我们返回最少的天数。因此提示我们还需要升序排序。因此我们可以先处理数据。

bloom_order = sorted(set(bloomDay))

? (2)遍历:

? 如果直接使用 for day in bloom_order:来进行排序,会超时。由于超过最小天数的天数也能够满足制作m束花的需求。因此满足二分的性质:单调性。因此可以使用二分查找来优化。

? (3)统计个数:

? 如果直接对每个位置都进行求取其后k个元素的值,会进行两层循环:

# O(N * k), 超时!!!
for i in range(len(bloomDay)):
    for j in range(k):
        # ...

? 还可以定义两个变量,分别保存可以制作的花的数量num,以及相邻k朵花中开放的花数量sums:

for i in range(len(bloomDay)):
    if num >= m:
        break
    if bloomDay[i] <= day:
        sum += 1
    else:
        sums = 0
    if sums == k:
        num += 1
    	sums = 0
return num >= m

代码(Python):

class Solution:
    def minDays(self, bloomDay: List[int], m: int, k: int) -> int:
        def check(day):
            num = sums = 0
            for i in range(len(bloomDay)):
                if num >= m:
                    break
                if bloomDay[i] <= day:
                    sums += 1
                else:
                    sums = 0
                if sums == k:
                    num += 1
                    sums = 0
            return num >= m

        length = len(bloomDay)
        if length < m * k:
            return -1

        bloom_order = sorted(set(bloomDay))
        left, right = 0, len(bloom_order) - 1
        while left <= right:
            mid = (left + right) // 2
            if check(bloom_order[mid]):
                right = mid - 1
            else:
                left = mid + 1

        return bloom_order[left]

四、1483. 树节点的第 K 个祖先:

给你一棵树,树上有 n 个节点,按从 0 到 n-1 编号。树以父节点数组的形式给出,其中 parent[i] 是节点 i 的父节点。树的根节点是编号为 0 的节点。

请你设计并实现 getKthAncestor(int node, int k) 函数,函数返回节点 node 的第 k 个祖先节点。如果不存在这样的祖先节点,返回 -1 。

树节点的第 k 个祖先节点是从该节点到根节点路径上的第 k 个节点。

示例:

技术图片

输入:
["TreeAncestor","getKthAncestor","getKthAncestor","getKthAncestor"]
[[7,[-1,0,0,1,1,2,2]],[3,1],[5,2],[6,3]]

输出:
[null,1,0,-1]

解释:
TreeAncestor treeAncestor = new TreeAncestor(7, [-1, 0, 0, 1, 1, 2, 2]);

treeAncestor.getKthAncestor(3, 1); // 返回 1 ,它是 3 的父节点
treeAncestor.getKthAncestor(5, 2); // 返回 0 ,它是 5 的祖父节点
treeAncestor.getKthAncestor(6, 3); // 返回 -1 因为不存在满足要求的祖先节点

分析:

? 看了官方题解。提到一种ACM的模板题解法——Binary Lifting。这里贴一个题解。之后遇到类似的题再仔细分析一下。

代码(Python):

class TreeAncestor:

    def __init__(self, n: int, parent: List[int]):
        self.cols = 20  # log(50000) < 20
        self.dp = [[-1] * self.cols for _ in range(n)]
        for i in  range(n):
            self.dp[i][0] = parent[i]
        for j in range(1, self.cols):
            for i in range(n):
                if self.dp[i][j - 1] != -1:
                    self.dp[i][j] = self.dp[self.dp[i][j - 1]][j - 1]
            return

    def getKthAncestor(self, node: int, k: int) -> int:
        for i in range(self.cols - 1, -1, -1):
            if k & (1 << i):
                node = self.dp[node][i]
                if node == -1:
                    break
        return node


# Your TreeAncestor object will be instantiated and called as such:
# obj = TreeAncestor(n, parent)
# param_1 = obj.getKthAncestor(node,k)























以上是关于Python解题 - CSDN周赛第43期的主要内容,如果未能解决你的问题,请参考以下文章

CSDN编程周赛第18期题解

CSDN编程周赛第18期题解

LeetCode周赛第193场周赛

LeetCode周赛第194场周赛

LeetCode周赛第195场周赛

[解题报告] CSDN竞赛第22期