Python | 蓝桥杯系列文章总结+经典例题重做

在这里插入图片描述
欢迎交流学习~~


专栏: 蓝桥杯Python组刷题日寄

4 个月前开始写蓝桥杯系列,到目前为止一共是 19 篇,其中:入门篇 5 篇,简单篇 8 篇,进阶篇 6 篇


这篇文章主要是为了为先前内容进行总结,并对其中的部分经典例题进行回顾。


蓝桥杯入门系列:

😜【蓝桥杯入门篇】Python组刷题日寄Part01
😗【蓝桥杯入门篇】Python组刷题日寄Part02
😮【蓝桥杯入门篇】Python组刷题日寄Part03
😍【蓝桥杯入门篇】Python组刷题日寄Part04
😃【蓝桥杯入门篇】Python组刷题日寄Part05

蓝桥杯简单系列:

💙【蓝桥杯简单篇】Python组刷题日寄Part01
💜【蓝桥杯简单篇】Python组刷题日寄Part02
💚【蓝桥杯简单篇】Python组刷题日寄Part03
💗【蓝桥杯简单篇】Python组刷题日寄Part04
💕【蓝桥杯简单篇】Python组刷题日寄Part05
💘【蓝桥杯简单篇】Python组刷题日寄Part06
💖【蓝桥杯简单篇】Python组刷题日寄Part07
💔【蓝桥杯简单篇】Python组刷题日寄Part08

蓝桥杯进阶系列:

🏆 Python | 蓝桥杯进阶第一卷——字符串
🔎 Python | 蓝桥杯进阶第二卷——贪心
💝 Python | 蓝桥杯进阶第三卷——动态规划
✈️ Python | 蓝桥杯进阶第四卷——图论
🌞 Python | 蓝桥杯进阶第五卷——数论
💎 Python | 蓝桥杯进阶第六卷——搜索


Python | 蓝桥杯系列文章总结+经典例题重做

  • ☔ 蚂蚁感冒
  • ❄️ Sine之舞
  • 🐯 完美的代价
  • 🐧 字符串展开
  • 🌴 Huffman树
  • 🍁 2^k进制数
  • 🌏 卡勒沃夫之弱水路三千
  • 🏀 盾神与砝码称重
  • 🍏 麦森数
  • 🎁 危险系数
  • 💡 2n皇后问题
  • 🚲 学霸的迷宫

接下来我们从上面的文章中选取一定量的题目,进行回顾总结,并在原先基础上进行改进。


☔ 蚂蚁感冒

来源:💙【蓝桥杯简单篇】Python组刷题日寄Part01

题目:
时间限制:
1s

内存限制:
128MB

题目描述:
100 厘米的细长直杆子上有 n 只蚂蚁。它们的头有的朝左,有的朝右。
每只蚂蚁都只能沿着杆子向前爬,速度是 1 厘米/秒。
当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。
这些蚂蚁中,有 1 只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。
请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。

输入描述:
第一行输入一个整数 n(1 < n < 50), 表示蚂蚁的总数。
接着的一行是n个用空格分开的整数 Xi(-100 < Xi < 100), Xi的绝对值,表示蚂蚁离开杆子左边端点的距离。正值表示头朝右,负值表示头朝左,数据中不会出现 0 值,也不会出现两只蚂蚁占用同一位置。其中,第一个数据代表的蚂蚁感冒了。

输出描述:
要求输出 1 个整数,表示最后感冒蚂蚁的数目。

样例输入:

5
-10 8 -20 12 25

样例输出:
3


解题思路

首先我们要计算两个值:leftright
left 表示第一只感冒蚂蚁的左侧, 头向右蚂蚁的数量
right 表示第一只感冒蚂蚁的右侧, 头向左蚂蚁的数量

其次,我们经过分析得到,感冒要传染,只能通过相遇,而不能通过追击

对于第一只感冒的蚂蚁,我们分为两种情况讨论:

  • 头向左
    • 若其左侧头向右的蚂蚁,则左侧的 left 只蚂蚁会依次逐个传染;而第一只感冒蚂蚁会转头,会使右侧的 right 只蚂蚁依次逐个传染;
    • 若其左侧没有头向右的蚂蚁,则不会有蚂蚁传染。
  • 头向右
    • 若其右侧头向左的蚂蚁,则右侧的 right 只蚂蚁会依次逐个传染;而第一只感冒蚂蚁会转头,会使左侧的 left 只蚂蚁依次逐个传染;
    • 若其右侧没有头向左的蚂蚁,则不会有蚂蚁传染。

参考代码

n = int(input())
dis = list(map(int, input().split()))

# k 为第一只感冒蚂蚁距离左边的距离
k = abs(dis[0])

# left 表示第一只感冒蚂蚁的左侧, 头向右蚂蚁的数量
# right 表示第一只感冒蚂蚁的右侧, 头向左蚂蚁的数量
left = right = 0

# 计算最终感冒蚂蚁
for i in dis[1:]:
    # 对于第一只感冒蚂蚁的左侧, 如果头向右
    if 0 < i < k:
        left += 1
    # 对于第一只感冒蚂蚁的右侧, 如果头向左
    if i < 0 and abs(i) > k:
        right += 1

# 如果第一只感冒的蚂蚁头向左, 且 left 不为 0
# 或 第一只感冒的蚂蚁头向右, 且 right 不为 0
if (dis[0] < 0 and left != 0) or (dis[0] > 0 and right != 0):
    print(left + right + 1)
else:
    print(1)


❄️ Sine之舞

来源:💚【蓝桥杯简单篇】Python组刷题日寄Part03

题目:
时间限制:
1s

内存限制:
128MB

题目描述:
最近FJ为他的奶牛们开设了数学分析课,FJ知道若要学好这门课,必须有一个好的三角函数基本功。所以他准备和奶牛们做一个“Sine之舞”的游戏,寓教于乐,提高奶牛们的计算能力。
不妨设
A n = s i n ( 1 – s i n ( 2 + s i n ( 3 – s i n ( 4 + . . . s i n ( n ) ) . . . ) An=sin(1–sin(2+sin(3–sin(4+...sin(n))...) An=sin(1–sin(2+sin(3–sin(4+...sin(n))...)

S n = ( . . . ( A 1 + n ) A 2 + n − 1 ) A 3 + . . . + 2 ) A n + 1 Sn=(...(A1+n)A2+n-1)A3+...+2)An+1 Sn=(...(A1+n)A2+n1)A3+...+2)An+1
FJ想让奶牛们计算 Sn 的值,请你帮助FJ打印出 Sn 的完整表达式,以方便奶牛们做题。

输入描述:
仅有一个数:N < 201

输出描述:
请输出相应的表达式 Sn,以一个换行符结束。输出中不得含有多余的空格或换行、回车符。

样例输入:

3

样例输出:

((sin(1)+3)sin(1-sin(2))+2)sin(1-sin(2+sin(3)))+1

解题思路

本题可以分别构造两个函数,将两者表示即可,其中对于 An,其函数表示为:

def A(n):
    # res 用来记录 An 结果
    res = ''
    for i in range(1, n+1):
        res += 'sin(' + str(i)
        if i != n:
            if i%2 != 0:
                res += '-'
            else:
                res += '+'

    # 结尾的 n 个反括号
    res += ')' * n
    return res

而对于 Sn 有:

def S(n):
    # res 用来记录 Sn 的结果
    res = ''
    # 开头的 n-1 个正括号
    res += '(' * (n-1)

    for i in range(1, n+1):
        res += str(A(i)) + '+' + str(n-i+1)
        if i != n:
            res += ')'

    return res

之后就直接利用这两个函数即可。


参考代码

def A(n):
    # res 用来记录 An 结果
    res = ''
    for i in range(1, n+1):
        res += 'sin(' + str(i)
        if i != n:
            if i%2 != 0:
                res += '-'
            else:
                res += '+'

    # 结尾的 n 个反括号
    res += ')' * n

    return res

def S(n):
    # res 用来记录 Sn 的结果
    res = ''
    # 开头的 n-1 个正括号
    res += '(' * (n-1)

    for i in range(1, n+1):
        res += str(A(i)) + '+' + str(n-i+1)
        if i != n:
            res += ')'

    return res

if __name__ == '__main__':
    n = int(input())
    print(S(n))

🐯 完美的代价

来源:🏆 Python | 蓝桥杯进阶第一卷——字符串

题目:
时间限制:
1s

内存限制:
128MB

题目描述:
回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
交换的定义是:交换两个相邻的字符
例如:对于 mamad
第一次交换 ad : mamda
第二次交换 md : madma
第三次交换 ma : madam (回文!完美!)

输入描述:
第一行是一个整数 N,表示接下来的字符串的长度(N < = 8000)
第二行是一个字符串,长度为 N,只包含小写字母

输出描述:
如果可能,输出最少的交换次数。
否则输出 Impossible

样例输入:

5
mamad

样例输出:
3


解题思路

本题利用双指针,其中头指针 head 从前向后遍历,尾指针 tail 从后向前遍历。

退出循环的条件:

  • 字符串长度为偶数,但是有频数为奇数的字母
  • 字符串长度为奇数,但是有多于 1 个的频数为奇数的字母

具体思路见参考代码。

参考代码

n = int(input())
s = list(input())
count = 0   # 记录交换次数
flag = 0    # 标记是否有字母频数为奇数
res = True	# 标记是否为Impossible(是Impossible置为False)

# 双指针
L = n - 1   # 头指针搜索范围,到倒数第2个
for head in range(L):
    # 对于每一个s[head],用尾指针去搜索是否有相同的字母s[tail]
    for tail in range(L, head - 1, -1):
        # 指针tail搜索完毕,没有发现s[head] == s[tail]
        # 说明该字母频数为奇数 1,且该字母在回文序列中一定在中间
        if head == tail:
            # 如果字符串长度为偶数
            # 字符串长度为奇数,但是已经有 flag == 1
            if n%2 == 0 or flag == 1:
                res = False
                break
            flag = 1
            # (n//2 - head) 为将该字母移动到中间位置的交换次数
            count += n//2 - head
        # 发现了 s[head] == s[tail]
        elif s[head] == s[tail]:
            # 交换位置
            for i in range(tail, L):
                s[i], s[i+1] = s[i+1], s[i]
                count += 1
            L -= 1
            break

    # 如果是impossible,退出外层循环
    if res == False:
        break

if res == False:
    print('Impossible')
else:
    print(count)

🐧 字符串展开

来源:🏆 Python | 蓝桥杯进阶第一卷——字符串

题目:
时间限制:
1s

内存限制:
128MB

题目描述:
在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于 d-h 或者 4-8 的字串,我们就把它当作一种简写,输出时,用连续递增的字母获数字串替代其中的减号,即,将上面两个子串分别输出为 defgh45678。在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活。具体约定如下:

  • 遇到下面的情况需要做字符串的展开:在输入的字符串中,出现了减号 -,减号两侧同为小写字母或同为数字,且按照ASCII码的顺序,减号右边的字符严格大于左边的字符。
  • 参数p1:展开方式。p1=1 时,对于字母子串,填充小写字母;p1=2 时,对于字母子串,填充大写字母。这两种情况下数字子串的填充方式相同。p1=3 时,不论是字母子串还是数字字串,都用与要填充的字母个数相同的星号 * 来填充。
  • 参数p2:填充字符的重复个数。p2=k表示同一个字符要连续填充 k 个。例如,当 p2=3 时,子串d-h 应扩展为 deeefffgggh。减号两边的字符不变。
  • 参数p3:是否改为逆序:p3=1 表示维持原来顺序,p3=2 表示采用逆序输出,注意这时候仍然不包括减号两端的字符。例如当p1=1p2=2p3=2时,子串 d-h 应扩展为 dggffeeh
  • 如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:d-e 应输出为 de3-4 应输出为 34。如果减号右边的字符按照 ASCII码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:d-d 应输出为 d-d3-1 应输出为 3-1

输入描述:
输入包括两行:
1 行为用空格隔开的 3 个正整数,一次表示参数 p1p2p3
2 行为一行字符串,仅由数字、小写字母和减号 - 组成。行首和行末均无空格。

数据规模和约定
100%的数据满足:1<=p1<=31<=p2<=81<=p3<=2。字符串长度不超过 100

输出描述:
输出只有一行,为展开后的字符串.

样例输入:

1 2 1
abcs-w1234-9s-4zz

样例输出:
abcsttuuvvw1234556677889s-4zz


解题思路

  • 符号 - 只会在 s[1:len(s)-1] 之间(s 为输入字符串),因此要在这个范围内遍历,找到符号 - 并将其展开;
  • 满足展开的条件,即要判断左右两边的字符的ASCII码关系;
  • 之后根据 p3->p1->p2 的判断顺序对展开字符进行修改。

具体见代码。

参考代码

p1, p2, p3 = map(int, input().split())
s = input()
res = s[0]
# '-' 只会在s[1:len(s)-1]中间,因此s的首尾不需要变化
for i in range(1, len(s)-1):
    left, mid, right = s[i-1], s[i], s[i+1]
    # 遇到 '-' 并且左右字符满足如下条件:
    # 条件1:left<right
    # 条件2:(left>='0' and right<='9') or (left>='a' and right<='z') 
    if mid == '-' and left < right and ((left >= '0' and right <= '9') or (left >= 'a' and right <= 'z')):
        ## 判断顺序:p3 -> p1 -> p2
        ## p3
        if p3 == 1:
            # 顺序
            index_j = [j for j in range(ord(left)+1, ord(right))]
        else:
            # 逆序
            index_j = [j for j in range(ord(right)-1, ord(left), -1)]
        
        ## p1
        for j in index_j:
            # chr()转为字符
            tmp = chr(j)
            if p1 == 2:
                # 变为大写
                tmp = tmp.upper()
            elif p1 == 3:
                # 变为 '*'
                tmp = '*'
            
            ## p2
            # 重复 p2 次
            res += tmp * p2
    else:
            res += mid

# 尾巴
res += s[-1]
print(res)

🌴 Huffman树

来源:🔎 Python | 蓝桥杯进阶第二卷——贪心

题目:
时间限制:
3s

内存限制:
192MB

题目描述:
Huffman树在编码中有着广泛的应用。在这里,我们只关心Huffman树的构造过程。

给出一列数 {pi}={p0, p1, …, pn-1},用这列数构造Huffman树的过程如下:

  1. 找到 {pi} 中最小的两个数,设为 papb,将 papb{pi} 中删除掉,然后将它们的和加入到 {pi} 中。这个过程的费用记为 pa + pb

  2. 重复步骤1,直到 {pi} 中只剩下一个数。

在上面的操作过程中,把所有的费用相加,就得到了构造Huffman树的总费用。

本题任务:对于给定的一个数列,现在请你求出用该数列构造Huffman树的总费用。

例如,对于数列 {pi}={5, 3, 8, 2, 9},Huffman树的构造过程如下:

  1. 找到 {5, 3, 8, 2, 9} 中最小的两个数,分别是 23,从 {pi} 中删除它们并将和 5 加入,得到 {5, 8, 9, 5},费用为 5

  2. 找到 {5, 8, 9, 5} 中最小的两个数,分别是 55,从 {pi} 中删除它们并将和 10 加入,得到{8, 9, 10},费用为 10

  3. 找到 {8, 9, 10} 中最小的两个数,分别是 89,从 {pi} 中删除它们并将和 17 加入,得到 {10, 17},费用为 17

  4. 找到 {10, 17} 中最小的两个数,分别是 1017,从 {pi} 中删除它们并将和 27 加入,得到 {27},费用为 27

  5. 现在,数列中只剩下一个数 27,构造过程结束,总费用为 5+10+17+27=59

输入描述:
输入的第一行包含一个正整数 n(n< =100)

接下来是 n 个正整数,表示 p0, p1, …, pn-1,每个数不超过 1000

输出描述:
输出用这些数构造Huffman树的总费用。

样例输入:

5 
5 3 8 2 9

样例输出:
59


解题思路

排序后循环 n-1 次,每次选出前两个(最小值),计算其和后,再加入列表中,并将这两个最小值删除。

参考代码

n = int(input())
nums = list(map(int, input().split()))
res = 0
nums.sort()

for i in range(n-1):
    total = nums[0] + nums[1]
    nums.append(total)
    res += total
    nums.pop(0)
    nums.pop(0)
    nums.sort()
print(res)

🍁 2^k进制数

来源:💝 Python | 蓝桥杯进阶第三卷——动态规划

题目:
时间限制:
1s

内存限制:
128MB

题目描述:
r r r 是个 2 k 2^k 2k 进制数,并满足以下条件:

  • r r r 至少是个 2 2 2 位的 2 k 2^k 2k 进制数。
  • 作为 2 k 2^k 2k 进制数,除最后一位外, r r r 的每一位严格小于它右边相邻的那一位。
  • r r r 转换为 2 2 2 进制数 q q q 后,则 q q q 的总位数不超过 w w w

在这里,正整数 k ( 1 ≤ k ≤ 9 ) k(1≤k≤9) k(1k9) w ( k < w ≤ 30000 ) w(k<w≤30000) w(k<w30000) 是事先给定的。

问:满足上述条件的不同的 r r r 共有多少个?
我们再从另一角度作些解释:设 S S S 是长度为 w w w 的 01字符串(即字符串 S S S w w w01组成), S S S 对应于上述条件中的 q q q。将 S S S 从右起划分为若干个长度为 k k k 的段,每段对应一位 2 k 2^k 2k 进制的数,如果 S S S 至少可分成 2 2 2 段,则 S S S 所对应的二进制数又可以转换为上述的 2 k 2^k 2k 进制数 r r r

例:设 k = 3 , w = 7 k=3,w=7 k=3w=7。则 r r r 是个八进制数 ( 2 3 = 8 ) (2^3=8) (23=8)。由于 w = 7 w=7 w=7,长度为 7 的 01 字符串按 3位一段分,可分为 3 段(即1,3,3,左边第一段只有一个二进制位),则满足条件的八进制数有:

2位数:
高位为16 个(即 12,13,14,15,16,17 ),高位为 25 个,…,高位为61个(即67 )。共 6+5+…+1=21 个。

3位数:
高位只能是1,第 2 位为 25 个(即123,124,125,126,127),第2位为34个,…,第 2 位为 61个(即 167)。共5+4+…+1=15个。
所以,满足要求的 r r r 共有 36个。

输入描述:
只有1行,为两个正整数,用一个空格隔开:
k k k w w w

输出描述:
1行,是一个正整数,为所求的计算结果,即满足条件的不同的 r r r 的个数(用十进制数表示),要求最高位不得为0,各数字之间不得插入数字以外的其他字符(例如空格、换行符、逗号等)。
(提示:作为结果的正整数可能很大,但不会超过200位)

样例输入:
3 7
样例输出:
36


解题思路

确定 dp 数组及其下标的含义:
dp[i][j] 表示有 (i+1) 位数字,最高位数字是 j 时满足条件的数字个数。

确定递推公式:
j!=0dp[i][j] = dp[i-1][j+1]+dp[i-1][j+2]+dp[i-1][j+3]······
也就是说最高位不是0的时候,满足条件的数字个数是,第二高位的数字大于最高位的数字的情况的和。
j==0dp[i][j] = dp[i-1][j]+dp[i-1][j+1]+dp[i-1][j+2]+dp[i-1][j+3]······
如果最高位是 0,那么第二高位数字则可以取0。也就比上一个情况多加了个 dp[i-1][j]

初始化 dp 数组:
dp[0][j]=1 即只有一位数字时只有一种情况

确定遍历顺序:
顺序遍历即可。

此外注意最终结果的计算和处理,具体见代码。


参考代码

import math
k, w = map(int, input().split())
# rows, cols 为dp数组的行数和列数
rows, cols = w//k+1, 2**k
# i 是 2^k 进制下数字的位数
# j 是 每一位上可以取到的值
# dp[i][j] 表示有 (i+1) 位数字,最高位数字是 j 时满足条件的数字个数
dp = [[0 for j in range(cols)] for i in range(rows)]
# 初始化 res,为结果
res = 0

# dp 数组初始化
# dp[0][j] 表示有 1 位数字的情况
for j in range(cols):
    dp[0][j] = 1

# 递推
for i in range(1, rows):
    for j in range(cols):
        dp[i][j] = sum(dp[i-1][j+1:])
        if j == 0:
            dp[i][j] += dp[i-1][j]

# 计算最后的结果
if w%k == 0:
    # 此时最高位可以取 0 到 cols, 即最后一行所有情况都可以用
    res = sum(dp[-1])
else:
    # 此时最高位只能取 0 到 2**(w%k)-1
    res = sum(dp[-1][:2**(w%k)])

# 题目中要求位数 >= 2
# 需要减去只有一位数的情况
if w/k <= 1:
    res = 0
else:
    res -= 2**k
print(res)

🌏 卡勒沃夫之弱水路三千

来源:✈️ Python | 蓝桥杯进阶第四卷——图论

题目:
时间限制:
1s

内存限制:
128MB

题目描述:
锦瑟年华谁与度 莫问情归处 只影向斜阳 剑吼西风 欲把春留驻
天涯芳草无归路 回首花无数 解语自销魂 弱袂萦春 尘缘不相误

在卡勒沃夫充满文学杀伤力的声音中,身处紫荆2号楼202B的四位远近高低各不同的室友纷纷回忆起了各自波澜起伏的过去,并对长在百草园,邻有百花谷的现状表达了各自的见解。
某Q:" …我小学就开窍了…她的父母说我很好,但是…今天又和北林的联系了…"
某X:" …差点就成了,结果到学校了…这个方法放假了我去对我的同桌用!…"
某W:" …" (千言万语不言中,有大量的故事等待考古)
某Z:" …为了来清华…咱们审美观不一样,不会抢…"

卡勒沃夫在这个不朽的夜话中搜集出了某人零散的历任女友资料,为了强迫某人将他出的题目的标程交出,现在卡勒沃夫需要一个能将这些零散信息整合起来的程序。

输入描述:
第一行为一个不超过 5 的整数 T,表示数据的组数。之后每组数据的一行为一个不超过 100 的整数 n。之后 n 行每行有两个用单个空格隔开的字符串(每个字符串只有英文大小写字母,长度不超过 10),为两位 mm 的名字。每行第一个 mm 先于第二个 mm 成为某人的女友。
在这里我们假装诅咒某人不会同时被两个或两个以上 mm 泡,某个 mm 抛弃了某人后不会再吃回头草,同时卡勒沃夫深邃的洞察力使得他收集到了充足的信息以确定某人女友的先后顺序。
在小数据组中出现的人物不超过 13 个.

输出描述:
输出 T 行,每行对应一组数据,并按照 mm 们从先到后成为某人女友的顺序输出她们的名字,各个名字间用一个空格隔开。

样例输入:

2 
2 
RY  Unknown 
YSZ  RY 
3 
tomorrow  yestoday 
tomorrow  today 
today  yestoday 

样例输出:

YSZ RY Unknown
tomorrow today yestoday

解题思路

本题思路是拓扑排序。

具体思路见参考代码代码和注释。


参考代码

from collections import defaultdict
"""
defaultdict 是 collections 模块中的一个类。
它继承自 Python 内置的 dict 类
但是与 dict 类不同的是,defaultdict 会自动为访问的不存在的键赋一个默认值。
这样在使用 defaultdict 时就不需要像在使用 dict 时那样先判断一个键是否存在,再进行对应的操作。
"""
class Graph:
    def __init__(self):
        # 每个键对应的值为一个列表,用来存储该键值作为起点对应的终点
        self.graph = defaultdict(list)

    def addEdge(self, u, v):
        # 添加一条以 u 为起点,v 为终点的边
        self.graph[u].append(v)

    # 拓扑排序函数
    def topoLogicalSortUtil(self, key, visited, stack):
        # 标记该结点为已访问
        visited[key] = True
        for i in self.graph[key]:
            if visited[i] == False:
                self.topoLogicalSortUtil(i, visited, stack)
        # 用一个栈来保存排序结点
        stack.append(key)

    def topoLogicalSort(self, dic):
        visited = dic.copy()
        stack = []
        for key in dic.keys():
            # 判断该结点是否被访问过
            if visited[key] == False:
                # 如果没有被访问过
                self.topoLogicalSortUtil(key, visited, stack)

        # 栈是先进后出,因此需要倒序输出
        for i in stack[::-1]:
            print(i, end=' ')
        print()


t = int(input())
while t:
    # dic 用来保存结点状态:是否被访问过
    # start, end 分别存储起点和终点
    dic, start, end = {}, [], []
    n = int(input())
    for i in range(n):
        a, b = input().split()
        start.append(a)
        end.append(b)

    # grils 利用集合去重,可以得到所有结点(即所有前女友名字)
    grils = set(start + end)

    # 创建 Graph 类
    gf_graph = Graph()

    # 添加边
    for i in zip(start, end):
        gf_graph.addEdge(i[0], i[1])

    # 初始化结点状态
    for i in grils:
        dic.update({i: False})

    # 拓扑排序
    gf_graph.topoLogicalSort(dic)
    t -= 1

🏀 盾神与砝码称重

来源:✈️ Python | 蓝桥杯进阶第四卷——图论

题目:
时间限制:
1s

内存限制:
128MB

题目描述:
有一天,他在宿舍里无意中发现了一个天平!这个天平很奇怪,有n个完好的砝码,但是没有游码。盾神为他的发现兴奋不已!于是他准备去称一称自己的东西。他准备好了 m 种物品去称。神奇的是,盾神一早就 知道这 m 种物品的重量,他现在是想看看这个天平能不能称出这些物品出来。但是盾神稍微想了1秒钟以后就觉得这个问题太无聊了,于是就丢给了你。

输入描述:
第一行为两个数,nm
第二行为 n 个数,表示这 n 个砝码的重量。
第三行为 m个数,表示这 m 个物品的重量。

数据规模和约定
1 <= n <= 24, 1 <= m <= 10.
输出描述:
输出 m 行,对于第 i 行,如果第 i 个物品能被称出,输出 YES 否则输出 NO

样例输入:

4 2
1 2 4 8
15 16

样例输出:

YES
NO

解题思路

深度优先搜索。

注意每个砝码有三种状态:

  • 砝码和物品放在不同侧;
  • 不使用该砝码
  • 砝码和物品放在同一侧

对于每一个称重物品,逐个考虑砝码的三种状态,进行搜索。

具体见参考代码和注释。


参考代码

# 深度优先搜索
def dfs(num, res):
    global weight, tag, w01
    # 退出条件:达到物品的重量 -- 成功
    if res == weight:
        tag = True
        return
    # 退出条件:没有砝码可用 -- 失败
    elif num < 0:
        return
    else:
        ## 搜索下一个砝码
        # 将这个砝码放在物品的不同侧
        dfs(num - 1, res + int(w01[num]))
        # 不使用第 i 个砝码
        dfs(num - 1, res)
        # 将这个砝码和物品放在同一侧
        dfs(num - 1, res - int(w01[num]))

if __name__ == '__main__':
    # 输入
    n, m = map(int, input().split())
    # w01, w02 分别为砝码和物品重量
    w01 = list(map(int, input().split()))
    w02 = list(map(int, input().split()))

    # 对于每一个物品
    for i in w02:
        weight = i
        tag = False
        dfs(n-1, 0)

        # 输出结果
        if tag:
            print('YES')
        else:
            print('NO')

🍏 麦森数

来源:🌞 Python | 蓝桥杯进阶第五卷——数论

题目:
时间限制:
1s

内存限制:
128MB

题目描述:
任何一个正整数都可以用 2 的幂次方表示。例如:
137=2^7+2^3+2^0
同时约定方次用括号来表示,即 ab 可表示为 a(b)

由此可知,137 可表示为:
2(7)+2(3)+2(0)
进一步:7= 2^2+2+2^02^12 表示)
3=2+2^0
所以最后 137 可表示为:
2(2(2)+2+2(0))+2(2+2(0))+2(0)

又如:
1315=2^10+2^8+2^5+2+2^0
所以 1315 最后可表示为:
2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

输入描述:
输入包含一个正整数 N(N<=20000),为要求分解的整数。

输出描述:
程序输出包含一行字符串,为符合约定的 n02 表示(在表示中不能有空格)

样例输入:
1315

样例输出:
2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)


解题思路

可以通过每个数的二进制来得到其二次幂分解:
比如样例中的 1315,其对应的二进制数为:0b10100100011
其中 1 对应的位置由高到低为:11 9 6 2 1
因此其对应二次幂分解为:1315 = 2^10 + 2^8 + 2^5 +2 ^1 + 2^0

对于 0 次幂和 1 次幂特别处理,而对于高次幂,通过递归调用。
注意:要从高次幂开始处理。

具体见参考代码及其注释。

参考代码

def trans(n):
    # 转为 2 进制后再处理
    tmp = list(bin(n))
    # 去除 2 进制前面的 0b
    n = tmp[2:]
    # 转换为整数
    n = [int(i) for i in n]
    res = ''
    for i in range(1, len(n) + 1):
        if n[len(n) - i]:
            if len(n) - i == 0:
                # 处理 0 次幂
                res += '2(0)+'
            elif len(n) - i == 1:
                # 处理 1 次幂
                res += '2+'
            else:
                # 其余情况,递归调用
                res += '2(' + trans(len(n) - i) + ')+'

    # 最后res会有一个 '+' 需要去除
    return res[:-1]

if __name__ == '__main__':
    n = int(input())
    print(trans(n))

🎁 危险系数

来源:💎 Python | 蓝桥杯进阶第六卷——搜索

题目:
时间限制:
1s

内存限制:
128MB

题目描述:
抗日战争时期,冀中平原的地道战曾发挥重要作用。
地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。
我们来定义一个危险系数 DF(x,y)
对于两个站点 x y (x != y), 如果能找到一个站点 z,当 z 被敌人破坏后,xy 不连通,那么我们称 z 为关于 x,y 的关键点。相应的,对于任意一对站点 xy,危险系数 DF(x,y) 就表示为这两点之间的关键点个数。
本题的任务是:已知网络结构,求两站点之间的危险系数。

输入描述:
输入数据第一行包含 2 个整数 n(2 <= n <= 1000), m(0 <= m <= 2000) ,分别代表站点数,通道数;
接下来 m 行,每行两个整数 u,v (1 < = u, v < = n; u != v) 代表一条通道;
最后 1 行,两个数 u,v,代表询问两点之间的危险系数 DF(u, v)

输出描述:
一个整数,如果询问的两点不连通则输出 -1.

样例输入:

7 6
1 3
2 3
3 4
3 5
4 5
5 6
1 6

样例输出:
2


解题思路

深度优先搜索,对于每条从 uv 的路径,对于路径上经过的每个结点 i,其到达 v 的路径数 path[i] 都记 +1,搜索结束后若 path[i]==path[v] 则说明这个点为关键点。

具体思路见参考代码及其注释。

参考代码

# 深度优先搜索
def dfs(u, v):
    global path
    # 退出条件
    if u == v:
        # 当访问到最后的站点 end
        for i in range(n):
            # 遍历所有站点
            if visited[i] == True:
                # 如果这个站点在这条路线上
                path[i] += 1
        return
    for i in range(n):
        if mat[u][i] == 1 and not visited[i]:
            # 当 start 和 i 两站点连通
            # 且站点 i 未被访问
            visited[i] = True
            # 递归
            dfs(i, v)
            # 回溯
            visited[i] = False


if __name__ == '__main__':
    n, m = map(int, input().split())
    # mat 为邻接矩阵
    mat = [[0 for j in range(n)] for i in range(n)]
    for i in range(m):
        a, b = map(int, input().split())
        mat[a-1][b-1] = 1
        mat[b-1][a-1] = 1

    # u, v 两个节点
    u, v = map(int, input().split())
    u, v = u-1, v-1

    # visited[i] 表示站点 i 是否被访问
    visited = [False for i in range(n)]
    # path[i] 表示经过站点 i 到终点 v 的路径数
    path = [0 for i in range(n)]

    dfs(u, v)
    # path[v]为从 u 到 v 的路径数
    # 当其他站点也为这个数时,说明该站点为关键点
    print(path.count(path[v]) - 1)
    # 这里只需要 -1 是表示 v 结点
    # 而不用考虑 u 结点,是因为我们设置邻接矩阵时有 mat[i][i] = 0

💡 2n皇后问题

来源:💎 Python | 蓝桥杯进阶第六卷——搜索

题目:
时间限制:
1s

内存限制:
128MB

题目描述:
给定一个 n*n 的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入 n 个黑皇后和 n 个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n 小于等于 8

输入描述:
输入的第一行为一个整数 n,表示棋盘的大小。 n 小于等于 8
接下来 n 行,每行 n01 的整数,如果一个整数为 1,表示对应的位置可以放皇后,如果一个整数为 0,表示对应的位置不可以放皇后。

输出描述:
输出一个整数,表示总共有多少种放法。

样例输入:


4
1 1 1 1 
1 1 1 1 
1 1 1 1 
1 1 1 1 

样例输出:
2


解题思路

根据题意我们得到:每行必然有一个黑皇后和一个白皇后。
我们的思路是:先将各行的黑皇后放好,再将各行的白皇后放好。

定义 s 为皇后状态,s=2 为黑皇后,s=3 为白皇后。
即对于 cell 表示各结点状态:有 0 表示不可以放皇后,1 表示可以放皇后,2 表示黑皇后,3 表示白皇后。

定义 check(row, col, s, cell) 函数,用来判断坐标 (row, col)s 对应的皇后是否可行。
定义深度优先搜索函数 dfs(row, n, s, cell) 搜索第 row 行(最多有 n 行)是否可以放 s 对应的皇后。

参考代码

n = int(input())
cell = [list(map(int, input().split())) for i in range(n)]
count = 0

# check 函数,判断皇后放的位置是否满足条件
def check(row, col, s, cell):
    # 检查对应列是否有 s
    for i in range(row-1, -1, -1):
        if cell[i][col] == s:
            return False
    # 检查左对角线
    r = row - 1
    c = col - 1
    while r >= 0 and c >= 0:
        if cell[r][c] == s:
            return False
        r -= 1
        c -= 1

    # 检查右对角线
    r = row - 1
    c = col + 1
    while r >= 0 and c < n:
        if cell[r][c] == s:
            return False
        r -= 1
        c += 1
    return True

# dfs 函数
def dfs(row, n, s, cell):
    global count
    # 判断是否到最后一行
    # 这里是 n 表示,row == n-1 时已经搜索完毕
    # 再次递归时有 row == n,这是说明已经到底
    if row == n:
        # 若为黑皇后
        if s == 2:
            # 则接下来从第一行开始放白皇后
            dfs(0, n, 3, cell)
        # 若为白皇后
        # 此时黑白皇后已经全部放完
        # 退出
        if s == 3:
            count += 1
        return

    # 遍历每行
    for col in range(n):
        # 如果 cell[row][col] 不能再放皇后
        if cell[row][col] != 1:
            continue

        # 如果该结点可以放皇后
        if check(row, col, s, cell):
            # 改变结点状态
            cell[row][col] = s
            # 递归搜索下一行
            dfs(row+1, n, s, cell)
            # 回溯
            cell[row][col] = 1

if __name__ == '__main__':
    dfs(0, n, 2, cell)
    print(count)

🚲 学霸的迷宫

来源:💎 Python | 蓝桥杯进阶第六卷——搜索

题目:
时间限制:
1s

内存限制:
128MB

题目描述:
学霸抢走了大家的作业,班长为了帮同学们找回作业,决定去找学霸决斗。但学霸为了不要别人打扰,住在一个城堡里,城堡外面是一个二维的格子迷宫,要进城堡必须得先通过迷宫。因为班长还有妹子要陪,磨刀不误砍柴功,他为了节约时间,从线人那里搞到了迷宫的地图,准备提前计算最短的路线。可是他现在正向妹子解释这件事情,于是就委托你帮他找一条最短的路线。

输入描述:
第一行两个整数 nm,为迷宫的长宽。
接下来n行,每行 m 个数,数之间没有间隔,为 01 中的一个。0 表示这个格子可以通过,1 表示不可以。假设你现在已经在迷宫坐标 (1,1) 的地方,即左上角,迷宫的出口在 (n,m) 。每次移动时只能向上下左右 4 个方向移动到另外一个可以通过的格子里,每次移动算一步。数据保证 (1,1)(n,m) 可以通过。

输出描述:
第一行一个数为需要的最少步数 K
第二行 K 个字符,每个字符 ∈{U,D,L,R},分别表示上下左右。如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。

样例输入:

3 3
001
100
110

样例输出:

4
RDRD

解题思路

本题利用广度优先搜索。

注意题目中要求:

如果有多条长度相同的最短路径,选择在此表示方法下字典序最小的一个。

因此我们搜索的顺序是 D, L, R, U,即:下左右上

思路与上文中穿越雷区相同。

参考代码

# 广度优先搜索
n, m = map(int, input().split())
cell = [list(map(int, input())) for i in range(n)]

# visited用来记录结点访问状态
visited = [[False for j in range(m)] for i in range(n)]
visited[0][0] = True

# dx, dy 表示 下左右上 方向的坐标变动
d = ['D', 'L', 'R', 'U']
dx = [1, 0, 0, -1]
dy = [0, -1, 1, 0]

# res 用来记录当前搜索层结点的结果
# res[0][0] 表示横坐标
# res[0][1] 表示纵坐标
# res[0][2] 表示到达该结点的移动次数
# res[0][3] 表示到达该结点的路径
res = [[0, 0, 0, []]]

while res:
    # 终止条件:到达目标结点
    if (res[0][0] == n-1) and (res[0][1] == m-1):
        print(res[0][2])
        print(''.join(res[0][3]))

    # 遍历四个方向
    for i in range(4):
        new_x = res[0][0] + dx[i]
        new_y = res[0][1] + dy[i]
        new_path = res[0][3] + [d[i]]

        # 如果新坐标不超过边界
        if (0 <= new_x <= n-1) and (0 <= new_y <= m-1):
            # 如果该结点没有被访问过,且可以通过
            if (not visited[new_x][new_y]) and cell[res[0][0]][res[0][1]] == 0:
                res.append([new_x, new_y, res[0][2]+1, new_path])
                visited[new_x][new_y] = True

    # 该结点搜索完毕
    res.pop(0)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/3683.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot 将PDF转成图片或World

SpringBoot 将PDF转成图片或World 准备工作Apache PDFBox将PDF转成一张图片将PDF转成多张图片将PDF转成其他文件格式总结SpringBoot 是一款非常流行的 Java Web 开发框架,可以用来构建各种 Web 应用程序。在本篇博客中,我们将介绍如何使用 SpringBoot 将 PDF 转换成图片或其他…

五、MyBatis各种查询功能

MyBatis的各种查询功能 如果查询出的数据只有一条&#xff0c;可以通过 实体类对象接收List集合接收Map集合接收 如果查询出的数据有多条&#xff0c;一定不能用实体对象接收&#xff0c;会抛TooManyResultsException&#xff0c;可以通过 实体类类型的List集合接收Map类型的L…

怎么设计一个秒杀系统

1、系统部署 秒杀系统部署要单独区别开其他系统单独部署&#xff0c;这个系统的流量肯定很大&#xff0c;单独部署。数据库也要单独用一个部署的数据库或者集群&#xff0c;防止高并发导致整个网站不可用。 2、防止超卖 100个库存&#xff0c;1000个人买&#xff0c;要保证不…

【数据结构】树和二叉树的介绍

文章目录前言一、树1.1 树的概念1.2 树的相关概念1.3 树的表示1.4 树的用途二、二叉树2.1 二叉树的概念2.2 两种特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储方式总结前言 树是一种让程序员们既爱又恨的数据结构。它就像是一棵大树&#xff0c;让你可以轻松地摘取其中的果实…

基于 Docker 的深度学习环境:入门篇

这篇文章聊聊如何从零到一安装、配置一个基于 Docker 容器的深度学习环境。 写在前面 这段时间&#xff0c;不论是 NLP 模型&#xff0c;还是 CV 模型&#xff0c;都得到了极大的发展。有不少模型甚至可以愉快的在本地运行&#xff0c;并且有着不错的效果。所以&#xff0c;经…

【LeetCode】链表练习 9 道题

第一题&#xff1a;移除链表元素 题目描述&#xff1a; 给你一个链表的头节点head和一个整数val&#xff0c;请你删除链表中所有满足Node.val val的节点&#xff0c;并返回新的头节点 。 列表中的节点数目在范围 [0, 10^4] 内1 < Node.val < 500 < val < 50 /…

从零开始学OpenCV——图像灰度变换详解(线性与非线性变换)

文章目录图像灰度变化灰度变换介绍灰度线性变换灰度分段线性变换图像点运算灰度非线性变换线性点运算灰度的非线性变换&#xff1a;对数变换灰度的非线性变换&#xff1a;伽马变换灰度的非线性变换&#xff1a;对比拉伸灰度的非线性变换&#xff1a; S形灰度变换灰度的非线性变…

小程序逆向工程:这个开源的小程序逆向工具真不错,2023年亲测成功

前言 安全部门的大哥又双叒叕报了一个小程序的高危漏洞&#xff0c;他使用逆向工程破解了加密信心&#xff0c;用抓包修改了请求参数。又是头疼的一天… 想成为一名微信小程序的开发者&#xff0c;前端思路的学习和安全意识是非常有必要的&#xff0c;故务必掌握小程序反编译…

使用txt编写Java代码并通过cmd命令执行

文章目录前言一、创建一个.txt文件二、编写Java代码三、保存文件四、打开cmd命令窗口五、通过cmd命令编译生成.class文件六、通过cmd命令运行代码前言 若要用cmd命令执行Java文件&#xff0c;首先我们需要在机器上配置好JDK环境 在此附上JDK1.8安装文档超详细JDK1.8安装与配置 …

计算机网络笔记——物理层

计算机网络笔记——物理层2. 物理层2.1 通信基础2.1.1 信号2.1.2 信源、信道及信宿2.1.3 速率、波特及码元2.1.4 带宽2.1.5 奈奎斯特定理采样定理奈奎斯特定理2.1.6 香农定理2.1.7 编码与调制调制数字信号调制为模拟信号模拟数据调制为模拟信号编码数字数据编码为数字信号模拟数…

【python实操】年轻人,别用记事本保存数据了,试试数据库吧

为什么用数据库&#xff1f; 数据库比记事本强在哪&#xff1f; 答案很明显&#xff0c;你的文件很多时候都只能被一个人打开&#xff0c;不能被重复打开。当有几百万数据的时候&#xff0c;你如何去查询操作数据&#xff0c;速度上要快&#xff0c;看起来要清晰直接 数据库比我…

【数据结构与算法】堆与堆排序

目录一.堆的实现1.堆的概念2.堆的代码实现二.堆排序的讲解一.堆的实现 1.堆的概念 堆是一种数据结构&#xff0c;首先它总是一颗完全二叉树(因为堆适合表示完全二叉树)&#xff0c;在逻辑上堆是一颗完全二叉树&#xff0c;真正实现上是使用数组来实现的。根据不同的规则(任意…

【算法基础】一篇文章彻底弄懂Dijkstra算法|多图解+代码详解

博主简介&#xff1a;努力学习的大一在校计算机专业学生&#xff0c;热爱学习和创作。目前在学习和分享&#xff1a;算法、数据结构、Java等相关知识。博主主页&#xff1a; 是瑶瑶子啦所属专栏: 算法 &#xff1b;该专栏专注于蓝桥杯和ACM等算法竞赛&#x1f525;近期目标&…

【linux】深入了解TCP与UDP

认识端口号 端口号(port)是传输层协议的内容. 端口号是一个2字节16位的整数; 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理; IP地址 端口号能够标识网络上的某一台主机的某一个进程; 一个端口号只能被一个进程占用理解 "端口号" 和…

数据结构MySQL —— 索引

目录 一、索引概述 二、索引结构 三、索引分类 四、索引语法 五、SQL性能分析 1. 查看执行频次 2. 慢查询日志 3. show profiles指令 4. explain执行计划 六、索引使用规则 1. 验证索引效率 2. 最左前缀法则 3. 范围查询 4. 索引失效情况 5. SQL提示 6. …

记录springboot+vue+fastdfs实现简易的文件(上传、下载、删除、预览)操作

前言说明&#xff1a;springboot vue FastDFS实现文件上传&#xff08;支持预览&#xff09;升级版 FASTDFS部分 FASTDFS安装过程&#xff1a;基于centos 7安装FastDFS文件服务器 SpringBoot部分 springboot源码实现 package com.core.doc.controller;import com.baomid…

Spring Boot集成RocketMQ实现普通、延时、事务消息发送接收、PULL消费模式及开启ACL | Spring Cloud 30

一、前言 在前面我们通过以下章节对RocketMQ有了基础的了解&#xff1a; docker-compose 搭建RocketMQ 5.1.0 集群&#xff08;双主双从模式&#xff09; | Spring Cloud 28 docker-compose 搭建RocketMQ 5.1.0 集群开启ACL权限控制 | Spring Cloud 29 现在开始我们正式学习…

LORA: LOW-RANK ADAPTATION OF LARGE LAN-GUAGE MODELS

Paper name LORA: LOW-RANK ADAPTATION OF LARGE LAN-GUAGE MODELS Paper Reading Note Paper URL: https://arxiv.org/pdf/2106.09685.pdf Code URL: huggingface 集成&#xff1a; https://github.com/huggingface/peft官方代码&#xff1a; https://github.com/microsof…

C++11新特性

C11新特性 1统一列表初始化 1.1{} 在C98中&#xff0c;标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。 C11扩大了用大括号括起的列表(初始化列表)的使用范围&#xff0c;使其可用于所有的内置类型和用户自定义的类型&#xff0c;**使用初始化列表时&…

安全防御之入侵检测篇

目录 1.什么是IDS&#xff1f; 2.IDS和防火墙有什么不同&#xff1f;3.IDS的工作原理&#xff1f; 4.IDS的主要检测方法有哪些&#xff1f;请详细说明 5.IDS的部署方式有哪些&#xff1f; 6.IDS的签名是什么意思&#xff1f;签名过滤器有什么用&#xff1f;例外签名的配置作…
最新文章