2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

这篇具有很好参考价值的文章主要介绍了2020第十一届蓝桥杯Python组国赛【真题+解析+代码】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🎁2020第十一届蓝桥杯python组国赛真题



🚀 真题练习,冲刺国赛 🚀

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

2020年第十一届蓝桥python组国赛真题+解析+代码

博观而约取,厚积而薄发

🏆国赛真题目录


试题A.美丽的2⭐️

🍰1.题目

美丽的2
2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

👑2.思路分析

难度:⭐️

标签:枚举&字符串

🎇思路:暴力法

🔱思路分析:

本题为填空题,只需要遍历 1 → 2020 1→2020 12020即可:

  1. 将数字转为字符串 ( s t r ) (str) (str)
  2. 如果 ′ 2 ′ '2' 2 在字符串 s s s中,则结果 + 1 +1 +1

💯3.代码实现

cnt=0
for i in range(1,2021):
    s=str(i)
    if '2' in s:
        cnt+=1
print(cnt) # 563

输出结果:
2020第十一届蓝桥杯Python组国赛【真题+解析+代码】



试题B.合数个数⭐️

🍰1.题目

合数个数
2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

👑2.思路分析

难度:⭐️

标签:枚举

🎇思路①:试除法

🔱思路分析:

本题为填空题,因此可以直接使用暴力法得出结果

  1. 由于 1 1 1 2 2 2均为素数,所以只需遍历 3 → 2020 3→2020 32020
  2. 当遍历到一个数时,我们对其进行判断:
    2 → x 2→\sqrt{x} 2x 进行试除
    ②如果整除,则说明有除了 1 1 1和本身外的其他因子存在,为合数;否则,为素数
  3. 返回值为 1 1 1,则为合数, c n t + 1 cnt+1 cnt+1


🎇思路②:素数筛

🔱思路分析:

对于任意一个素数,它的正整数倍 ( ≥ 2 ) (≥2) (2)一定是合数

  1. 构造 v i s vis vis数组,先初始化 v i s vis vis T r u e True True T r u e True True为素数, F a l s e False False为合数)
  2. 遍历 2 → 2020 2→2020 22020,若当前的数 x x x T r u e True True(未被标记),则为素数,对他进行处理:
    遍历 x x x的倍数 i = 2 x , 3 x , . . . i=2x,3x,... i=2x,3x,...
    ②如果 i i i未被标记过(防止合数重复标记,如2的3倍为6,3的2倍也为6,则统计的合数个数会增多),则标记vis[i]=False,并让 c n t + 1 cnt+1 cnt+1

图解:
2020第十一届蓝桥杯Python组国赛【真题+解析+代码】


💯3.代码实现

1.试除法实现

from math import sqrt

def not_prime(x):
    for i in range(2,int(sqrt(x))+1):
        if x%i==0:
            return 1 # 为合数
    return 0 # 为素数

cnt=0
for x in range(3,2021):
    if not_prime(x):
        cnt+=1
print(cnt) # 合数个数:1713

2.素数筛实现

vis=[True]*2021 # 默认都是素数
cnt=0
for x in range(2,2021):
    if vis[x]: # 如果是素数(没有被标记过)
        for i in range(x+x,2021,x): # 将[x,2020]中素数x的倍数全部标记为合数
            if vis[i]==True:
                cnt+=1
                vis[i]=False
print(cnt) # 1713

输出结果:

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】



试题C.阶乘约数⭐️⭐️⭐️

🍰1.题目

阶乘约数

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

👑2.思路分析

难度:⭐️⭐️⭐️

标签:数论——唯一分解定理

🎇思路①:暴力法

🔱思路分析:

要得到所有的因子,不难想到用暴力法统计,但是!!不出意外地超时了

step

  1. 遍历 i = 1 → 100 ! i=1→\sqrt{100!} i=1100! ,不断试除
  2. 如果能被整除,我们对该数进行讨论:
    ①如果 i = 100 ! i=\sqrt{100!} i=100! ,则这里因子只增加了一个,cnt+=1
    ②如果 i i i为其他数,则这里因子必然有两个,cnt+=2

不通过代码:

from math import sqrt
num=1
for i in range(2,101):
    num*=i

cnt=0
for j in range(1,int(sqrt(num))+1):
    if num%j==0:
        if j==int(sqrt(num)):
            cnt+=1
        else:
            cnt+=2
print(cnt)


🎇思路②:唯一分解定理

🔱思路分析:

我们先来介绍一个与此题相关的数学定理:

🚀 整数的唯一分解定理:

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

即正整数n(>1)可分解为: n = p 1 α 1 ∗ p 2 α 2 ∗ . . . ∗ p k α k n=p_1^{α1}*p_2^{α2}*...*p_k^{αk} n=p1α1p2α2...pkαk

其中, p 1 < p 2 < … < p k p1<p2<…<pk p1<p2<<pk皆素数, α i ( i = 1 , 2 , … , k ) αi (i=1,2,…,k) αi(i=12k)皆为正整数,而正整数n的正因数个数为: n u m = ( α 1 + 1 ) ∗ ( α 2 + 1 ) ∗ . . . ∗ ( α k + 1 ) num=(α1+1)*(α2+1)*...*(αk+1) num=(α1+1)(α2+1)...(αk+1),也就是将素数的次方加1后求乘积

为什么?

因为对于 p 1 p_1 p1来说,它的指数为 α 1 α1 α1,则分解时它可以有 α 1 + 1 α1+1 α1+1种取法;同理, α 2 α2 α2 α 2 + 1 α2+1 α2+1种取法;…;所以,总的因数个数为 ( α 1 + 1 ) ∗ ( α 2 + 1 ) ∗ . . . ∗ ( α k + 1 ) (α1+1)*(α2+1)*...*(αk+1) (α1+1)(α2+1)...(αk+1)


什么意思呢?

举个例子:

我们要求100的正因数个数:

  1. 将100分解为: 100 = 2 2 ∗ 5 2 100=2^2*5^2 100=2252
  2. p 1 = 2 , α 1 = 2 , p 2 = 5 , α 2 = 2 p_1=2,α1=2,p_2=5,α2=2 p1=2α1=2p2=5α2=2,所以正因数的个数为: ( 2 + 1 ) ∗ ( 2 + 1 ) = 9 (2+1)*(2+1)=9 (2+1)(2+1)=9
  3. 9 9 9个数即为: 1 , 2 , 4 , 5 , 10 , 20 , 25 , 50 , 100 {1,2,4,5,10,20,25,50,100} 1,2,4,5,10,20,25,50,100

有了上面的定理支撑,我们就可以用代码实现了:

step

  1. 由于 n ! = 1 ∗ 2 ∗ . . . ∗ n n!=1*2*...*n n!=12...n,所以,我们先找到 [ 1 , n ] [1,n] [1,n]中的质数,记录在数组 s s s
  2. 质因数分解:将每一个数都分解为质数的乘积,用字典记录 s s s中每一个素数的得到次数,最终即为指 α i αi αi
  3. 最后用公式: ( α 1 + 1 ) ∗ ( α 2 + 1 ) ∗ . . . ∗ ( α k + 1 ) (α1+1)*(α2+1)*...*(αk+1) (α1+1)(α2+1)...(αk+1)即可


💯3.代码实现

唯一分解定理实现:

from math import *

def is_prime(x):
    for i in range(2,int(sqrt(x))+1):
        if x%i==0:
            return 0 # 为合数
    return 1 # 为素数

s=[2] # 记录[1,100]的素数
for i in range(3,101):
    if is_prime(i):
        s.append(i)

p=dict()
for i in s:
    p[i]=0 # 将质数的次数初始化为0

# 质因数分解
for i in range(1,101):
    for a in s:
        while i%a==0:
            i=i/a
            p[a]+=1

# 求正因数个数
res=1
for d in p.values():
    res*=(d+1)
print(res)

输出结果:

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】



试题D.本质上升序列⭐️⭐️⭐️

🍰1.题目

本质上升序列

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】


👑2.思路分析

难度:⭐️⭐️⭐️

标签:dp动态规划

🎇思路:线性dp

🔱思路分析:

要求升序字符串,即字符串中从左到右满足线性关系(后一个大于前一个),因此,我们可以用动态规划从前到后不断统计个数

step

  1. 字符串为 s s s s [ k ] s[k] s[k]即代表字符串中第 k + 1 k+1 k+1个字符
  2. 动态规划数组 d p [ i ] dp[i] dp[i] d p [ i ] dp[i] dp[i]表示以字符 s [ i ] s[i] s[i]结尾的满足本质上升序列的字符串的个数,其中 i ∈ [ 1 , n − 1 ] i∈[1,n-1] i[1,n1],初始化 d p [ ] = 1 dp[]=1 dp[]=1
  3. 遍历 i i i 1 1 1 n − 1 n-1 n1,对于每一个 i i i,我们遍历i之前的所有字符 s [ j ] s[j] s[j]
    ①如果当前 s [ i ] > s [ j ] s[i]>s[j] s[i]>s[j]:则表明,以 s [ j ] s[j] s[j]结尾的满足本质上升序列的字符串,在结尾加上 s [ i ] s[i] s[i]后,也满足升序条件,且为一种新的字符串情况,因此:dp[i]+=dp[j]
    ②如果当前 s [ i ] = s [ j ] s[i]=s[j] s[i]=s[j]:则表明,对于 s [ j ] s[j] s[j]之前的字符,以 s [ j ] s[j] s[j]结尾和 s [ i ] s[i] s[i]结尾的本质是一样的,只是换了尾字符的位置,所以,要减去这些重复的情况:dp[i]-=dp[j]
    ③如果当前 s [ i ] < s [ j ] s[i]<s[j] s[i]<s[j]:则表明,不能直接向以 s [ j ] s[j] s[j]结尾的字符串后添加 s [ i ] s[i] s[i],因为不满足升序条件了,所以跳过,继续判断下一个 s [ j ] s[j] s[j]continue
  4. 最后,统计 d p dp dp数组 [ 0 , n − 1 ] [0,n-1] [0,n1]的和,即为所有情况

为什么要初始化 d p dp dp 1 1 1

  1. d p [ 0 ] dp[0] dp[0]需要:因为以s[0](第一个字符)结尾的情况必定有 1 1 1种,且后续 d p [ 1 → n ] dp[1→n] dp[1n]的值都是由 d p [ 0 ] → d p [ i − 1 ] dp[0]→dp[i-1] dp[0]dp[i1]递推而来的,所以初始化 d p [ 0 ] = 1 dp[0]=1 dp[0]=1
  2. 默认以 s [ i ] s[i] s[i]结尾的满足条件的字符串都是一种情况(也就是单个字符的情况):我们在遍历时,只遍历了 s [ i ] s[i] s[i]之前的 s [ 0 → ( i − 1 ) ] s[0→(i-1)] s[0(i1)]的情况,而没有考虑单个 s [ i ] s[i] s[i]自身,所以,要初始化为1,如果字符串 s s s中有单个重复的情况,也会在 s [ i ] = s [ j ] s[i]=s[j] s[i]=s[j]时被减去

以字符串 ′ l a n q i a o ′ 'lanqiao' lanqiao为例:
2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

特别注意,当 s [ 5 ] = s [ 1 ] = ′ a ′ s[5]=s[1]='a' s[5]=s[1]=a时,应该执行dp[5]-dp[1],这里可以直观地看到,就是减去’a’这一情况,避免了重复字符串的出现

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

以此类推,最终相加即可得到总数为: 1 + 1 + 3 + 6 + 2 + 0 + 8 = 21 1+1+3+6+2+0+8=21 1+1+3+6+2+0+8=21


💯3.代码实现

线性dp实现:

s='tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhfiadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqijgihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmadvrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl'
n=len(s)
dp=[1]*n # dp[i]表示以s[i]结尾的字符串中本质上升序列的数目(单个字符都满足)
for i in range(1,n):
    for j in range(i): # [0,i-1]
        if s[i]>s[j]: # 如果s[i]>s[j],则可以看做以s[j]结尾的字符串之后再添加一个s[i]便可以得到新的一种本质上升序列
            dp[i]+=dp[j]
        elif s[i]==s[j]: # 如果s[i]=s[j],则表示dp[j]的情况和dp[i]部分重复了,所以要减去这些情况
            dp[i]-=dp[j]
        else:
            continue # 如果s[i]<s[j],继续下一轮
print(sum(dp)) # 3616159

输出结果:

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】



试题E.玩具蛇⭐️⭐️⭐️

🍰1.题目

玩具蛇

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

👑2.思路分析

难度:⭐️⭐️⭐️

标签:dfs深度优先遍历

🎇思路:dfs深搜

🔱思路分析:

题目要求找到所有摆放蛇各个身段的情况,由于蛇身是连续的,也就是相邻的身段只能朝上下左右摆放,不能是对角线,所以,我们可以对蛇头(序号为 1 1 1)在每一个方格处都进行 d f s dfs dfs深搜操作,最终得到所有满足条件的情况


注意:对于相同形状,但存在在相同方格中,蛇的位置不同时,为不同的方案

图示:
2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

step

  1. 4 x 4 4x4 4x4方格中每一个格子都作为一次蛇头的位置(序号为1)进行 d f s dfs dfs
  2. d f s ( x , y , s t e p ) dfs(x,y,step) dfs(x,y,step):从蛇头出发,表示当前蛇尾所在位置为 ( x , y ) (x,y) (x,y),且蛇的身长为 s t e p step step
    ①结束条件:如果蛇的身长达到16 step=16,则表示该方案可行,cnt+=1
    ②深搜四个方向:如果蛇尾向该方向扩展后,仍在4x4方格内,且该方格未被标记过时: 标记 + 深搜 + 回溯 标记+深搜+回溯 标记+深搜+回溯
  3. 最后 c n t cnt cnt的值即为情况总数

注意:在 4 x 4 4x4 4x4方格中,每一轮以新的方格作为蛇头时,要重新构造 v i s [ 4 ] [ 4 ] vis[4][4] vis[4][4]数组,因为要清空之前 d f s dfs dfs留下的标记


💯3.代码实现

dfs实现:

def judge(x,y):
    if x>=0 and x<=3 and y>=0 and y<=3 and vis[x][y]==False: # 如果该点还未被访问过
        return True
    return False

def dfs(x,y,step):
    global cnt,vis
    # 结束条件
    if step==16:
        cnt+=1
        return

    for d in [(1,0),(-1,0),(0,1),(0,-1)]: # 对四个方向进行深搜
        nx,ny=x+d[0],y+d[1]
        if judge(nx,ny):
            # 标记
            vis[nx][ny]=True
            # dfs
            dfs(nx,ny,step+1)
            # 回溯
            vis[nx][ny]=False


cnt = 0
for i in range(4):
    for j in range(4):
        vis = [[False] * 4 for _ in range(4)]  # 存4x4方格图(注意:每次到一个新的点,必须要清空vis数组,重新dfs)
        vis[i][j] = True # 对当前点(蛇头)进行标记
        dfs(i,j,1)  # 对点(i,j)进行深搜,1表示:经过了(i,j)位置,为第一步
print(cnt)

输出结果:
2020第十一届蓝桥杯Python组国赛【真题+解析+代码】



试题F.天干地支⭐️

🍰1.题目

天干地支

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】


👑2.思路分析

难度:⭐️

标签:列表

🎇思路:模拟

🔱思路分析:

本题要求任意年份的天干地支 ( [ 1 , 9999 ] ) ([1,9999]) ([1,9999]),由于天干的周期为 10 10 10,地支的周期为 12 12 12,因此,我们可以根据周期的差异分别计算天干和地支

step

  1. 构造天干和地支的列表
    这里有一个小技巧 t i p s tips tips:我们可以先算出公元 0 0 0年的天干地支 → → 为庚申年
    于是,我们可以将天干地支列表的首元素变为 ′ g e n g ′ 'geng' geng ′ s h e n ′ 'shen' shen,之后则按照正常顺序放置,这样,我们就可以实现 年份除以相应周期的余数 = = =对应列表的下标
  2. 年份分别对 10 10 10 12 12 12取余:t=year%10;d=year%12
    再将对应列表下标中的元素取出,拼在一起即为结果

💯3.代码实现

列表操作实现:

Tiangan=['geng','xin','ren','gui','jia','yi','bing','ding','wu','ji']
Dizhi=['shen','you','xu','hai','zi','chou','yin','mou','chen','si','wu','wei']
year=int(input())
t=year%10
d=year%12
print(Tiangan[t]+Dizhi[d])

输出结果:

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】



试题G.重复字符串⭐️⭐️⭐️

🍰1.题目

重复字符串

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】


👑2.思路分析

难度:⭐️⭐️⭐️

标签:枚举

🎇思路:字符串分组

🔱思路分析:

要令字符串 s = s= s=某字符串 s ′ s' s重复 k k k次,且满足对字符串 s s s 的修改次数最少,因此,我们可以将字符串 s s s分成连续的 k k k 个子串,每一个子串的大小为 L = l e n ( s ) / / k L=len(s)//k L=len(s)//k (如果有余数则无解),我们对这 k k k个子串对齐摆放,按列处理即可

step

  1. 先将字符串 s s s 切片处理,也就是取长度为 L = l e n ( s ) / / k L=len(s)//k L=len(s)//k 的子串,如果有余数 l e n ( s ) % k ! = 0 len(s)\%k!=0 len(s)%k!=0,则无解;反之,则继续进行下一步

  2. 由于新字符串的长度为 L L L,也就是:
    [ 0 , l e n ( s ) − 1 ] [0,len(s)-1] [0,len(s)1]分成 [ 0 , L − 1 ] , [ L , 2 L − 1 ] , . . . , [ l e n ( s ) − L , l e n ( s ) − 1 ] [0,L-1],[L,2L-1],...,[len(s)-L,len(s)-1] [0,L1][L,2L1]...[len(s)L,len(s)1]


    如何得到这样一个 k k k x L L L 阶的矩阵呢?
    可以依次以 s [ 0 → ( L − 1 ) ] s[0→(L-1)] s[0(L1)]为起始, L L L为步长遍历整个字符串并取出对应位置的元素:即可得到一列的元素


  3. 我们按列进行处理,每得到一列的元素,我们将其放入字典中,判断哪一个字符出现的次数最多(为 n u m num num,则我们将出现次数少的其他元素全部改为该元素即可满足在该位置上修改的次数最少,其中,每一列至少需要修改的次数为: c n t = k − n u m cnt=k-num cnt=knum


    因为总共有 k k k行,所以减去包含次数最多元素的 n u m num num行后,剩下的 k − n u m k-num knum行需要修改

  4. 循环遍历 0 → L − 1 0→L-1 0L1列,统计每一列至少需要修改次数之和即为 r e s res res


图解:

e . g e.g e.g 以字符串 s = ′ a b c a b a b c a c a a ′ s='abcababcacaa' s=abcababcacaa 为例,假设 k = 4 k=4 k=4,也就是重复出现4次

我们可以得到: k = 4 , L = 12 / / 4 = 3 k=4,L=12//4=3 k=4L=12//4=3,按照上述方法,则字符串 s s s 分组为 4 4 4 x 3 3 3 阶矩阵
2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

我们对每一列进行字典统计操作,统计出现次数最多的字符和对应的次数
2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

所以,至少需要修改: 2 + 2 + 1 = 5 2+2+1=5 2+2+1=5 次,即将其变为 ′ a b a ′ 'aba' aba 重复 4 4 4次得到 s s s


💯3.代码实现

字符串分组实现:

k=int(input())
s=input()
res=0
if len(s)%k!=0: # 不能被整除
    print(-1) # 无解
else:
    l=len(s)//k # l是列数
    for i in range(l):  # 分别取首位置为[0,l-1]
        dic = {}  # 清空字典
        for j in range(i,len(s),l): # 以l为步长 构造同一列下的字符集字典
            dic[s[j]]=dic.get(s[j],0)+1
        val=dic.values()
        num=max(val) # 找到同一列中出现次数最多的数
        res+=k-num # k是行数 k-num即为要修改的最小数字个数
    print(res)

输出结果:

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】



试题H.答疑⭐️⭐️⭐️

🍰1.题目

答疑
2020第十一届蓝桥杯Python组国赛【真题+解析+代码】


👑2.思路分析

难度:⭐️⭐️⭐️

标签:贪心

🎇思路:贪心算法

🔱思路分析:

首先,由贪心算法可知,我们只需要让答疑消耗总时间越短的人越先答疑,即可实现发送消息的时刻之和最短

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

step

  1. 将发送消息前后所需要的时间视为两个部分:即 x i → 发消息 → y i x_i → 发消息 → y_i xi发消息yi,总时间为 z i = x i + y i z_i=x_i+y_i zi=xi+yi,嵌套列表存放每一个人的 [ x i , y i , z i ] [x_i,y_i,z_i] [xi,yi,zi]
  2. 依据 z i z_i zi进行从小到大的排序,当前同学发消息的时刻为 t i m e + x i time+x_i time+xi,遍历所有嵌套列表,每一轮循环结束后,都让时刻处于当前同学完全结束的时刻,也就是 t i m e = z 0 + z 1 + . . . + z i time=z_0+z_1+...+z_i time=z0+z1+...+zi,之后继续下一个同学的答疑
  3. 最终,时刻之和即为 r e s res res

证明:

现在我们来证明,为什么这道题可以用贪心算法:

d e f def def:首先,每个人的 s i s_i si a i a_i ai 都可以视为一个部分(也就是前期准备),记为 d d d;收拾时间即为 e e e

规定 P i P_i Pi 表示第 i i i 个人, x i x_i xi y i y_i yi分别对应了第 i i i个人的 d d d e e e z i = x i + y i z_i=x_i+y_i zi=xi+yi 对应第 i i i个人的答疑总时间

假设当前序列为 P 1 P 2 . . . P i P i + 1 . . . P n P_1P_2...P_iP_{i+1}...P_n P1P2...PiPi+1...Pn,令 j = i + 1 j=i+1 j=i+1

易得,交换 P i P_i Pi P j P_j Pj 顺序并不会影响除了二者之外的发送消息时间,所以交换后变化的只有二者的发送时间,而影响最终结果

假设除了 i , j i,j i,j 之外的人的发送时间之和为 S S S,则 r e s = S + P i 发送时间 + P j 发送时间 res=S+P_i发送时间+P_j发送时间 res=S+Pi发送时间+Pj发送时间

x 1 + y 1 + x 2 + y 2 + . . . + x i − 1 + y i − 1 x_1+y_1+x_2+y_2+...+x_{i-1}+y_{i-1} x1+y1+x2+y2+...+xi1+yi1 S i − 1 S_{i-1} Si1

①交换顺序前 P i P_i Pi 的发送时间为 S i − 1 + x i S_{i-1}+x_i Si1+xi P j P_j Pj的发送时间为 S i − 1 + x i + y i + x j S_{i-1}+x_i+y_i+x_j Si1+xi+yi+xj r e s = S + ( S i − 1 + x i ) + ( S i − 1 + x i + y i + x j ) res=S+(S_{i-1}+x_i)+(S_{i-1}+x_i+y_i+x_j) res=S+(Si1+xi)+(Si1+xi+yi+xj)
②交换顺序后 P i P_i Pi 的发送时间为 S i − 1 + x j + y j + x i S_{i-1}+x_j+y_j+x_i Si1+xj+yj+xi P j P_j Pj 的发送时间为 S i − 1 + x j S_{i-1}+x_j Si1+xj r e s ′ = S + ( S i − 1 + x j + y j + x i ) + ( S i − 1 + x j ) res'=S+(S_{i-1}+x_j+y_j+x_i)+(S_{i-1}+x_j) res=S+(Si1+xj+yj+xi)+(Si1+xj)

对比 r e s res res r e s ′ res' res,代入 z i , z j z_i,z_j zi,zj 后可以看出:

r e s = S + 2 S i − 1 + x i + x j + res=S+2S_{i-1}+x_i+x_j+ res=S+2Si1+xi+xj+ z j z_j zj
r e s ′ = S + 2 S i − 1 + x i + x j + res'=S+2S_{i-1}+x_i+x_j+ res=S+2Si1+xi+xj+ z i z_i zi

所以,影响结果的只有每个人答疑花费的总时间 ( z i z_i zi z j z_j zj),因此,只需要将总时间小的放在前面进行答疑即可(即对 z z z 进行排序)


💯3.代码实现

贪心算法实现:

n=int(input())
li=[]
for i in range(n):
    x,y,z=map(int,input().split())
    li.append([x+y,z,x+y+z])
li.sort(key=lambda x:x[2])  # 根据总时间排序,短的放前面
time=0
res=0
for i in range(n):
    time+=li[i][0] # 加上前期准备时间
    res+=time
    time+=li[i][1] # 加上离开时间
print(res)

输出结果:

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】



试题I.补给⭐️⭐️⭐️⭐️⭐️

🍰1.题目

补给
2020第十一届蓝桥杯Python组国赛【真题+解析+代码】


👑2.思路分析

难度:⭐️⭐️⭐️⭐️⭐️

标签: 状压 d p + F l o y d 状压dp+Floyd 状压dp+Floyd算法

🎇思路:状态压缩 d p + F l o y d dp+Floyd dp+Floyd算法

🔱思路分析:

本题为经典的"旅行商"问题:从起点出发,找一条经过所有n个点再回到起点的最短路径长度,于是,我们可以建模为带权无向图


step

1. F l o y d Floyd Floyd算法:

F l o y d Floyd Floyd算法思想是:由小图逐渐扩展为大图,不断向已有点的集合中加入一个新的点,依次判断该点的加入是否会对集合中的点之间的最短路径产生影响

  1. 用邻接矩阵 w [ i ] [ j ] w[i][j] w[i][j]储存路径:表示从点 i i i到点 j j j的最短路径
    初始时: w [ i ] [ j ] w[i][j] w[i][j]表示点 i i i到点 j j j之间的直接路径长度
    结束时: w [ i ] [ j ] w[i][j] w[i][j]表示点 i i i到点 j j j之间的最短路径长度


    注意:
    ①没有直接相连的两点默认值为无穷大
    w[i][i]=0


  1. 将第 1 1 1 n n n个点依次加入点集 S S S中,对图中每一个点对 ( i , j ) (i,j) i,j进行遍历,其中 i ∈ [ 1 , n ] , j ∈ [ 1 , n ] i∈[1,n],j∈[1,n] i[1,n],j[1,n],如果 w [ i ] [ j ] > w [ i ] [ k ] + w [ k ] [ j ] w[i][j]>w[i][k]+w[k][j] w[i][j]w[i][k]+w[k][j],也就是 i → k → j i→k→j ikj的距离比 i i i直接到 j j j的距离小,于是更新 i i i j j j 的最短路径 w [ i ] [ j ] w[i][j] w[i][j]

  2. 至此,我们就得到了无向图中任意两点间的最短距离


代码实现 F l o y d Floyd Floyd

def Floyd():
    global w
    for i in range(n): # 点0到n-1
        for j in range(n): # 表示点i到点j
            for k in range(n): # 引入第三点k
                w[i][j]=min(w[i][j],w[i][k]+w[k][j])

如图所示:

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】


2. 状态压缩 d p dp dp

对于上述问题,也就是给定一张n个点的带权无向图,点为 0 0 0 ~ n − 1 n-1 n1,现在已知任意两个点的最短路径,要求起点0到达每一个点后再回来的最短路径长度,也就是 0 0 0 ~ n − 1 n-1 n1个点都要经过

①确认状态:

d p dp dp动态规划:不难发现,我们只需要知道:①哪些点已经走过;②目前停在那个点上 即可,而此时不用关心已经遍历过的点之间的具体顺序,因为我们已经得到了经过当前点集 S S S中的点的最短路径


于是,我们设状态为 S S S d p [ S ] [ j ] dp[S][j] dp[S][j]表示起点 0 0 0到点 j j j,且经过点的点集 S S S 时的最短路径长度

则有 d p [ S ] [ j ] = d p [ S − j ] [ k ] + w [ k ] [ j ] dp[S][j]=dp[S-j][k]+w[k][j] dp[S][j]=dp[Sj][k]+w[k][j],其中 S − j S-j Sj表示点集 S S S中去除掉点 j j j后且包含点 k k k的集合, w [ k ] [ j ] w[k][j] w[k][j] 即为已经求得的点 k k k 到点 j j j 的最短路径长度

所以,当前新加入的点为 j j j,我们依次遍历 S − j S-j Sj中的点 k k k,找到 S − j S-j Sj j j j最短的一条路,再更新 d p [ S ] [ j ] dp[S][j] dp[S][j]的值即可,以此类推, S S S 则由 0 0 0 逐渐扩展到 n − 1 n-1 n1


图解算法:

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】


②状态压缩 d p dp dp

那么,我们应该如何表示这个集合 S S S呢?

这里就是状态压缩 d p dp dp的核心:用二进制数 0 / 1 0/1 0/1表示是否经过该点, 1 1 1表示经过, 0 0 0表示未经过

01101 01101 01101,表示经过了点 0 0 0 2 2 2 3 3 3,此时点集为: S = 0 , 2 , 3 S={0,2,3} S=0,2,3

所以, [ 1 , ( 1 < < n ) − 1 ] [1,(1<<n)-1] [1,(1<<n)1] 为每一个状态的点集 S S S ,最终为 111...1 ( n 个 1 ) 111...1 (n个1) 111...1(n1),表示所有点都经过了


  1. 初始化:dp[1][0]=0,表示只有起点时,到起点的最短距离自然为 0 0 0


  2. 对每个状态下的 S S S,遍历所有点 [ 0 , n − 1 ] [0,n-1] [0,n1],记为 j j j,判断当前 j j j 是否在当前点集 S S S 中:
    ①如果 j j j不在:即S>>j & 1==0 (按位与 ‘&’)
    则继续找下一个在集合中的 j j j 点;
    ②如果 j j j:即S>>j & 1!=0
    则找到没有点 j j j 时的集合 S − j S-j Sj,遍历 S − j S-j Sj 中的所有点 k k k,即满足:S^(1<<j)>>k &1 !=0 (按位异或 ‘^’)
    从而找到一个k,得到一条最短路径 w [ k ] [ j ] w[k][j] w[k][j],则有:dp[S][j]=dp[S-j][k]+w[k][j]


代码实现:

dp=[[float('inf')]*21 for _ in range(1<<21)]
dp[1][0]=0
for S in range(1,1<<n):
    for j in range(n):
        if S>>j & 1!=0: # 找到在集合S中的点j
            for k in range(n):
                if S^(1<<j)>>k &1 !=0: # 找到在集合S-j中的点k
                    dp[S][j]=min(dp[S][j],dp[S^(1<<j)][k]+w[k][j])

  1. 最后,找到经过所有点时的最佳停留点 i ( i ≠ 0 ) i(i≠0) i(i=0),使得此时走过的距离dp[(1<<n)-1][i],加上点 i i i 到起点 0 0 0的距离w[i][0]最短
res=float('inf')
for i in range(1,n):
    res=min(res,dp[(1<<n)-1][i]+w[i][0]) # 最短距离=min(到最终停留点i的距离+i到0的距离)

💯3.代码实现

状压 d p dp dp+ F l o y d Floyd Floyd算法实现:

from math import *
def dis(a,b):
    d=sqrt(pow(a[0]-b[0],2)+pow(a[1]-b[1],2))
    return d

def Floyd():
    global w
    for i in range(n): # 点0到n-1
        for j in range(n): # 表示点i到点j
            for k in range(n): # 引入第三点k
                w[i][j]=min(w[i][j],w[i][k]+w[k][j])


n,D=map(int,input().split())
w=[[float('inf')]*n for _ in range(n)] # 邻接矩阵记录最短路径
xy=[] # 记录坐标点
for i in range(n):
    xy.append(list(map(float,input().split())))

# 最短路径
for i in range(0,n):
    for j in range(i+1,n):
        d=dis(xy[i],xy[j]) # 传入两点的坐标
        if d<=D: # 如果两点的距离不超过最大飞行距离 更新
            w[i][j] = d
            w[j][i] = d
Floyd()

# 状态压缩
dp=[[float('inf')]*n for _ in range(1<<n)]
dp[1][0]=0
for S in range(1,1<<n):
    for j in range(n):
        if S>>j & 1!=0: # 找到在集合S中的点j
            for k in range(n):
                if S^(1<<j)>>k &1 !=0: # 找到在集合S-j中的点k
                    dp[S][j]=min(dp[S][j],dp[S^(1<<j)][k]+w[k][j])

res=float('inf')
for i in range(1,n):
    res=min(res,dp[(1<<n)-1][i]+w[i][0]) # 最短距离=min(到最终停留点i的距离+i到0的距离)
print("%.2f"%res)

输出结果:(python本身运行较慢,只能通过60%)

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】



试题J.蓝跳跳⭐️⭐️⭐️⭐️⭐️⭐️

🍰1.题目

蓝跳跳

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】


👑2.思路分析

难度:⭐️⭐️⭐️⭐️⭐️⭐️

标签: d p dp dp动态规划 +矩阵快速幂

🎇思路: d p dp dp +矩阵快速幂

🔱思路分析:

本题要求方案总数,因此考虑用 d p dp dp或者 d f s dfs dfs解决

此时会存在两个问题:

d p [ L ] [ k ] dp[L][k] dp[L][k],需要的空间很大( L = 1 0 18 L=10^{18} L=1018
L L L 太大,会超时

但不要着急,我们来一一解决


step

1.动态规划 d p dp dp

我们考虑直接 d p dp dp

定义 d p [ i ] [ j ] dp[i][j] dp[i][j] i i i表示当前跳到的位置, i i i 1 1 1递推到 L L L j j j 则表示走到 i i i 位置时最后一次走的步长为 j j j

则状态转移方程为:

d p [ i ] [ j ] = { ∑ d = 1 k d p [ i − j ] [ d ] j < p ∑ d = 1 p − 1 d p [ i − j ] [ d ] j ≥ p dp[i][j]=\begin{cases} ∑_{d=1}^{k}dp[i-j][d] & j<p \\ \\ ∑_{d=1}^{p-1}dp[i-j][d] & j≥p \end{cases} dp[i][j]= d=1kdp[ij][d]d=1p1dp[ij][d]j<pjp


也就是说,如果到位置 i i i 时:

  1. 最后一步的步长 j < p j<p j<p:那么可以选择 [ 1 , k ] [1,k] [1,k] 中的任意一个距离跳跃到 i − j i-j ij 位置,因为对于当前状态下来说,无论如何都不可能存在连续两次跳跃距离 > p >p >p
  2. 最后一步的步长 j ≥ p j≥p jp:那么此时只能选择 [ 1 , p − 1 ] [1,p-1] [1,p1] 中的一个距离跳跃到 i − j i-j ij 位置,因为这一步超过了 p p p,那么前一步就一定不能超过 p p p

此时的时间复杂度即为: O ( k p L ) O(kpL) O(kpL)

最终结果为: r e s = ∑ d = 1 k d p [ L ] [ d ] res=∑_{d=1}^kdp[L][d] res=d=1kdp[L][d]


接下来进行状态压缩<空间优化>:不难发现,对于 j j j,只有 < p <p <p ≥ p ≥p p 两种状态,我们不需要直到 j j j 的具体值,因此,将状态 j j j 压缩为 0 / 1 0/1 0/1,于是:

定义 d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1],表示当前跳到位置 i i i 0 / 1 0/1 0/1表示最后一步是否小于 p p p 的计数

状态转移方程变为:

d p [ i ] [ d ] = { ∑ j = 1 p − 1 d p [ i − j ] [ 0 ] + d p [ i − j ] [ 1 ] d = 0 ∑ j = p k d p [ i − j ] [ 0 ] d = 1 dp[i][d]=\begin{cases} ∑_{j=1}^{p-1}dp[i-j][0] + dp[i-j][1] & d=0 \\ \\ ∑_{j=p}^{k}dp[i-j][0] & d=1 \end{cases} dp[i][d]= j=1p1dp[ij][0]+dp[ij][1]j=pkdp[ij][0]d=0d=1


此时的时间复杂度为: O ( k L ) O(kL) O(kL)

最终结果为: r e s = d p [ L ] [ 0 ] + d p [ L ] [ 1 ] res=dp[L][0]+dp[L][1] res=dp[L][0]+dp[L][1]


2.矩阵快速幂

由上分析,我们发现,其状态转移 O ( k L ) O(kL) O(kL) 是线性的,也就是说,对于当前状态 i i i,只和前 k k k 个状态有关,且可以写成矩阵乘法的形式,具体地,我们写成 2 k 2k 2k x 2 k 2k 2k 矩阵

例如,当 k = 3 , p = 2 k=3,p=2 k=3p=2 时:


我们先列出线性方程组:

{   d p [ i ] [ 1 ] = d p [ i − 2 ] [ 0 ] + d p [ i − 3 ] [ 0 ]   d p [ i ] [ 0 ] = d p [ i − 1 ] [ 1 ] + d p [ i − 1 ] [ 0 ]   d p [ i − 1 ] [ 1 ] = d p [ i − 1 ] [ 1 ]   d p [ i − 1 ] [ 0 ] = d p [ i − 1 ] [ 0 ]   d p [ i − 2 ] [ 1 ] = d p [ i − 2 ] [ 1 ]   d p [ i − 2 ] [ 0 ] = d p [ i − 2 ] [ 0 ] \begin{cases} \ dp[i][1]=dp[i-2][0]+dp[i-3][0] \\ \ dp[i][0]=dp[i-1][1]+dp[i-1][0] \\ \ dp[i-1][1]=dp[i-1][1] \\ \ dp[i-1][0]=dp[i-1][0] \\ \ dp[i-2][1]=dp[i-2][1] \\ \ dp[i-2][0]=dp[i-2][0] \\ \end{cases}  dp[i][1]=dp[i2][0]+dp[i3][0] dp[i][0]=dp[i1][1]+dp[i1][0] dp[i1][1]=dp[i1][1] dp[i1][0]=dp[i1][0] dp[i2][1]=dp[i2][1] dp[i2][0]=dp[i2][0]


再表示为对应的矩阵乘法,也就实现了状态转移:

也就是由状态 i − 1 i-1 i1 扩展到了状态 i i i


( d p [ i ] [ 1 ] d p [ i ] [ 0 ] d p [ i − 1 ] [ 1 ] d p [ i − 1 ] [ 0 ] d p [ i − 2 ] [ 1 ] d p [ i − 2 ] [ 0 ] ) = ( 0 0 0 1 0 1 1 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 ) ( d p [ i − 1 ] [ 1 ] d p [ i − 1 ] [ 0 ] d p [ i − 2 ] [ 1 ] d p [ i − 2 ] [ 0 ] d p [ i − 3 ] [ 1 ] d p [ i − 3 ] [ 0 ] ) \left( \begin{matrix} dp[i][1] \\ dp[i][0] \\ dp[i-1][1] \\ dp[i-1][0] \\ dp[i-2][1] \\ dp[i-2][0] \end{matrix} \right) = \left( \begin{matrix} 0 & 0 & 0 & 1 & 0 & 1\\ 1 & 1 & 0 & 0 & 0 & 0\\ 1 & 0 & 0 & 0 & 0 & 0\\ 0 & 1 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 0 & 0 & 0\\ 0 & 0 & 0 & 1 & 0 & 0 \end{matrix} \right) \left( \begin{matrix} dp[i-1][1] \\ dp[i-1][0] \\ dp[i-2][1] \\ dp[i-2][0] \\ dp[i-3][1] \\ dp[i-3][0] \\ \end{matrix} \right) dp[i][1]dp[i][0]dp[i1][1]dp[i1][0]dp[i2][1]dp[i2][0] = 011000010100000010100001000000100000 dp[i1][1]dp[i1][0]dp[i2][1]dp[i2][0]dp[i3][1]dp[i3][0]


此时,时间复杂度为: O ( 8 k 3 l o g L ) O(8k^3logL) O(8k3logL)

(这里还没有做出来,先挖个坑吧…)


💯3.代码实现

动态规划 d p dp dp 实现:

k,p,L=map(int,input().split())
dp=[[0]*2 for _ in range(L+1)]
dp[0][0]=1
for i in range(1,L+1):
    # 1.考虑步长小于p时
    for j in range(1,p):
        if i-j<0:
            break
        dp[i][0]+=(dp[i-j][0]+dp[i-j][1])%20201114

    # 2.考虑步长大于等于p时
    for j in range(p,k+1):
        if i-j<0:
            break
        dp[i][1]+=dp[i-j][0]%20201114

res=sum(dp[L])%20201114
print(res)

输出结果:

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】


🎇希望备战蓝桥杯的小伙伴们都能有所收获,觉得有帮助的话,就赏个三连吧~🎇

如有错误,欢迎指正~!

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】文章来源地址https://www.toymoban.com/news/detail-467554.html

到了这里,关于2020第十一届蓝桥杯Python组国赛【真题+解析+代码】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第十一届国际分子模拟与人工智能应用学术会议 (2023-ICMS&AI)

    作为国内历史悠久、分子模拟领域公认的高水平国际学术会议,国际分子模拟与人工智能应用学术会议重磅回归。经过两年的精心筹备,本次会议将于 2023年5月6日-7日 在 成都 隆重举行,本次大会将为国内外从事分子模拟人工智能应用和研发创新数字化转型的企业、高校、科

    2023年04月26日
    浏览(22)
  • 2020年第十一届蓝桥杯省赛+解析(门牌制作、寻找2020、跑步锻炼、蛇形填数、排序、成绩统计、单词分析)

    目录 门牌制作 寻找2020 跑步锻炼 蛇形填数 排序 成绩统计

    2023年04月08日
    浏览(25)
  • 第十三届蓝桥杯Java B 组国赛 C 题——左移右移(AC)

    小蓝有一个长度为 N N N 的数组, 初始时从左到右依次是 1 , 2 , 3 , … N 1,2,3, ldots N 1 , 2 , 3 , … N 。 之后小蓝对这个数组进行了 M M M 次操作, 每次操作可能是以下 2 种之一: 左移 x x x , 即把 x x x 移动到最左边。 右移 x x x , 即把 x x x 移动到最右边。 请你回答经过 M M M 次操作之后

    2024年02月02日
    浏览(20)
  • [蓝桥杯单片机]——八到十一届初赛决赛客观题

     采用外部12MHz晶振,经过系统12分频时定时器获得最大定时长度,此时定时器定时脉冲为1MHz,周期为1s,而定时器计时均为 16位加法计数器,即计时长度为。 ①带阻滤波器是指能通过大多数频率分量、但将某些范围的频率分量衰减到极低水平的滤波器 ②低通滤波器是容许低

    2023年04月09日
    浏览(24)
  • 2022蓝桥杯C++B组国赛真题题解

    目录 A:2022 B:钟表 C:卡牌 D:最大数字 E:出差 F:费用报销 G:故障 H:机房 I:齿轮 J:搬砖 问题描述 将 2022 拆分成 10 个互不相同的正整数之和, 总共有多少种拆分方法? 注意交换顺序视为同一种方法, 例如 2022=1000+1022 和 1022+1000 就视为同一种方法。 答案提交 这是一道结果填空的

    2024年02月06日
    浏览(17)
  • 十三届蓝桥杯JAVA B组国赛部分题解

    大学总共参加了三届蓝桥杯,这应该是我最后一次参加蓝桥杯了,再写一篇题解算是给自己的业余竞赛结个尾。我的题解肯定不是最好的,甚至有许多错误,能给大家提供下思路就行。 思路:模拟下时钟就行,签到题 答案:502-8=494(由于匀速运动,59分59秒到0分0秒实际算一次

    2024年02月08日
    浏览(20)
  • 2020年“创享杯”第一届电子数据取证线上大比武部分Writeup

    近日,某市公安机关接到多名在校大学生报案,称其在做 “网上兼职刷单”被骗取金钱数额不等。经警方初步调查发现嫌疑人张某及同伙经常通过社交平台以高额回报为诱饵,套用真实刷单兼职工作流程,诱骗受害人多次汇款,涉嫌电信诈骗行为。经确认核实,警方对张某进

    2024年02月14日
    浏览(17)
  • 第十一章 Python第三方库纵览

    11.1 网络爬虫方向 网络爬虫是自动进行HTTP访问并捕获HTML页面的程序。Python语言提供了多个具备网络爬虫功能的第三方库。这里介绍两个常用的Python网络爬虫库: requests和scrapy 。 11.1.1 requests requests库是一个简洁且简单的处理HTTP请求的第三方库,其最大优点是程序编写过程更

    2024年02月08日
    浏览(18)
  • python笔记:第十一章正则表达式

    以一定规则,快速检索文本,或是实现一些替换操作 默认下,区分大小写 字符 描述 d 代表任意数字,就是阿拉伯数字 0-9 这些 D 代表非数字的字符。与d完全相反 w 代表字母,数字,下划线。也就是 a-z、A-Z、0-9、_ W 跟 w 相反 ,代表不是字母,不是数字,不是下划线的字

    2024年02月17日
    浏览(20)
  • 蓝桥杯青少组python:第十二届国赛

    1、设 s=\\\"Hi LanQiao\\\" ,运行一下哪个选项代码可以输出 \\\"LanQiao\\\" 子串() A、 print(S[-7:]) B、 print(s[-6:-1]) C、 print(s[-7:0]) D、 print(s[-7:0]) 2、已知 a=2021.0529 ,运行一下代码选项可以输出 2021.05 () A、 print(\\\"{2f}\\\".format(a)) B、 print(\\\"{:.2f}\\\".format(a)) C、 print(\\\"{2}\\\".format(a)) D、 print(\\\"{.2f}\\\".for

    2024年02月07日
    浏览(18)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包