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

Posted lijiancheng0614

tags:

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

CSDN编程竞赛报名地址:https://edu.csdn.net/contest/detail/37

1. 排查网络故障

题目

A地跟B地的网络中间有n个节点(不包括A地和B地),相邻的两个节点是通过网线连接。正常的情况下,A地和B地是可以连通的,有一天,A地和B地突然不连通了,已知只有一段网线出问题(两个相邻的节点)小明需要排查哪段网线出问题。他的排查步骤是:
1。 选择某个中间节点
2。 在这个节点上判断跟A地B地是否连通,用来判断那一边出问题

请问小明最少要排查多少次,才能保证一定可以找到故障网线

输入描述:

一个正整数 n (n <= 10^18),表示A地和B地之间的节点数

输出描述:

输出一个数字,代表保证一定可以找到故障网线的前提下,小明最少要排查多少次

输入样例:

2

输出样例:

2

解题报告

模拟,答案为 ⌈ l o g 2 ( n + 1 ) ⌉ \\lceil log_2(n + 1) \\rceil log2(n+1)⌉

class Solution:
    def __init__(self) -> None:
        pass

    def solution(self, n):
        if n < 2:
            return n
        import math
        n += 1
        result = int(math.log(n))
        while 2**result < n:
            result += 1
        return result


if __name__ == "__main__":
    n = int(input().strip())
    sol = Solution()
    result = sol.solution(n)
    print(result)

2. 零钱兑换

题目

给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币张数。 如果无解,请返回-1. 数据范围:数组大小满足 0 <= n <=10000 , 数组中每个数字都满足 0 < val <=10000,0 <= aim <=100000 要求:时间复杂度 O(n×aim) ,空间复杂度 O(aim)。

输入描述:

输入包括两行,第一行两个整数n(0<=n<=1000)代表数组长度和aim(0<=aim<=5000),
第二行n个不重复的正整数,代表arr(1 <= arri <=10^9)

输出描述:

输出一个整数,表示组成aim的最小货币数,无解时输出-1.

输入样例:

[5,2,3],20

输出样例:

4

解题报告

动态规划,设 f[i] 为组成 i 的最少货币数,初始时 f[i] = oo,则 f[i] = minf[i - j] + 1, 其中 j 遍历 arr 中每个值

若 f[aim] > oo 则答案为 -1 否则为 f[aim]

class Solution:
    def __init__(self) -> None:
        pass

    def solution(self, str1):
        arr, aim = eval(str1)
        f = [aim + 1] * (aim + 1)
        f[0] = 0
        for i in range(1, aim + 1):
            for j in arr:
                if j <= i:
                    f[i] = min(f[i], f[i - j] + 1)
        if f[aim] > aim:
            return -1
        return f[aim]


if __name__ == "__main__":
    str1 = input().strip()
    sol = Solution()
    result = sol.solution(str1)
    print(result)

3. 清理磁盘空间

题目

小明电脑空间满了,决定清空间。为了简化问题,小明列了他个人文件夹(/data)中所有末级文件路径和大小,挑选出总大小为 m 的删除方案,求所有删除方案中,删除操作次数最小是多少。

一次删除操作:删除文件或者删除文件夹。如果删除文件夹,那么该文件夹包含的文件都将被删除。
文件夹的大小:文件夹中所有末级文件大小之和

输入描述:

第一行输入 n (n <= 1000)和 m(m <= 1000),表示文件数量,和需要删除的大小

接下去有 n 行,每一行都是一个文件绝对路径(路径长度小于 100),和这个文件的大小(小于 1000)

输出描述:

输出所有删除方案中,删除操作次数最小是多少。如果找不到恰好删除的大小为 m 的方案,则打印 -1

输入样例:

6 10
/data/movie/a.mp4 5
/data/movie/b.mp4 3
/data/movie/c.mp4 2
/data/movie/d.mp4 4
/data/picture/a.jpg 4
/data/picture/b.jpg 1

输出样例:

2

解题报告

动态规划,把所有文件排序,设第 i 个文件名和大小分别为 x, y

设 f[i][j] 为删除前 i 个文件,总大小为 j 时的最少操作数,初始时 f[i][j] = oo,f[0][0] = 0

则 f[i][j] = minf[i - 1][j], f[i - 1][j - y] + 1,其中 1 <= j <= m

维护上一次的文件夹名称及编号 k 和文件夹总大小 yy,当 x 的文件夹名称与上一次不同时,则 f[i][j] = minf[i - 1][j], f[i - 1][j - y] + 1, f[k][j - yy] + 1

若 f[n][m] > oo 则答案为 -1 否则为 f[n][m]

class Solution:
    def __init__(self) -> None:
        pass

    def solution(self, n, m, vector):
        if m == 0:
            return 0
        if n == 0:
            return -1
        oo = n * 2
        f = [[oo for j in range(m + 1)] for i in range(n + 1)]
        vector = sorted(vector)
        zz = vector[0][0].split('/')[2]
        s = 0
        j = 0
        f[0][0] = 0
        for k, (x, y) in enumerate(vector):
            y = int(y)
            for i in range(m):
                f[k + 1][i] = min(f[k + 1][i], f[k][i])
                if f[k][i] < oo and i + y <= m:
                    f[k + 1][i + y] = min(f[k + 1][i + y], f[k][i] + 1)
            z = x.split('/')[2]
            if z != zz:
                for i in range(m):
                    if f[j][i] < oo and i + s <= m:
                        f[k + 1][i + s] = min(f[k + 1][i + s], f[j][i] + 1)
                s = y
                zz = z
                j = k
            else:
                s += y
        for i in range(m):
            if f[j][i] < oo and i + s <= m:
                f[k + 1][i + s] = min(f[k + 1][i + s], f[j][i] + 1)
        ans = min([f[k][m] for k in range(n + 1)])
        if ans < oo:
            return ans
        return -1


if __name__ == "__main__":
    arr_temp = [int(item) for item in input().strip().split()]
    n = int(arr_temp[0])
    m = int(arr_temp[1])
    vector = []
    for i in range(n):
        vector.append([item for item in input().strip().split()])
    sol = Solution()
    result = sol.solution(n, m, vector)
    print(result)

4. 交际圈

题目

小明参加了个大型聚会。聚会上有n个人参加,我们将他们编号为1…n,有些人已经互相认识了,有些人还不认识。聚会开始后,假设A跟B认识,A会给所有他认识的人介绍B,原先跟A认识,但不认识B的人,都会在此时,跟B互相认识。当所有人都把自己认识的人介绍一遍后,此时n个人就会形成k个交际圈,同一个交际圈中,两两互相认识,不同的交际圈之间,互相不认识
问题:当所有人都把自己认识的人介绍一遍后,形成了多少个交际圈

输入描述:

第1行包含两个数字n(n <= 100000), m(m <= 100000),n表示参加聚会的人数,m表示聚会前,有多少对人已经互相认识了
第2行到第m+1行,每一行包含两个数字,a和b(a != b, 1 <= a, b <= n),代表的是聚会前,a和b已经互相认识

输出描述:

输出一个数字,表示形成的交际圈的个数

输入样例:

3 3
1 2
1 3
2 3

输出样例:

1

解题报告

图论。用并查集统计有多少个连通块即可

class Solution:
    def __init__(self) -> None:
        pass

    def getf(self, x):
        if self.f[x] != x:
            self.f[x] = self.getf(self.f[x])
        return self.f[x]

    def solution(self, n, m, vector):
        self.f = [i for i in range(n + 1)]
        v = set()
        for x, y in vector:
            x = self.getf(self.f[x])
            y = self.getf(self.f[y])
            self.f[x] = y
        for i in range(1, n + 1):
            self.f[i] = self.getf(i)
        return len(set(self.f[1:]))


if __name__ == "__main__":
    arr_temp = [int(item) for item in input().strip().split()]
    n = int(arr_temp[0])
    m = int(arr_temp[1])
    vector = []
    for i in range(m):
        vector.append([int(item) for item in input().strip().split()])
    sol = Solution()
    result = sol.solution(n, m, vector)
    print(result)

以上是关于[解题报告] CSDN竞赛第23期的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

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

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