2021年软件类第十二届蓝桥杯 省赛 python组 A-E题解
文章目录
- 2021年软件类第十二届蓝桥杯 省赛 python组 A-E题解
- 试题 A:卡片
- 思路
- 代码
- 答案
- 试题 B:直线
- 思路
- 代码
- 答案
- 试题 C:货物摆放
- 思路
- 代码
- 答案
- 试题 D:路径
- 思路
- 代码
- 答案
- 试题 E:回路计数
- 思路
- 代码
- 答案
备战蓝桥杯的时候,记录一下写的题目和一些思考和理解
这里面的题目都可以从https://www.lanqiao.cn/courses/2786/learning/?id=280833得到,写了一些python的解法
除了本身的题解之外,也可以配套视频看一下吧,在我空闲之余做了这个的讲解,希望对大家有帮助,B站讲解视频
试题 A:卡片
思路
对于这道题来说,我局的思路还是比较清晰的,我们首先知道自己的卡片个多少张,我们可以利用一个哈希表,只要我们在拼一个数字的时候,我们的哈希表的值为0了,也就是我们的客片不够了,我们就直接输出我们的结果
对于python来说,我们可以很容易的去用我们的字典,我们的字典就是我们的哈希表,代码还是很简单的
代码
# https://www.lanqiao.cn/problems/1443/learning/
h = {}
n = 2021 # 卡片的数目
for i in range(10):
h[str(i)] = n # 定义每张卡片的数目
flag = True
i = 0
while flag:
i += 1
for x in str(i):
h[x] -= 1 # 拼x这个数需要用掉一张卡片
if h[x] < 0: # 卡片少于0,说明第i张卡片拼不了
flag = False
break
print(i-1)
答案
3181
试题 B:直线
思路
对于这道题来说,我们就需要根据我们学的数学公式,也就是我们的斜率和截距公式
对于每一条直线来说,最重要的就是他的斜率和截距,如果我们单单以斜率来看的话,就忽略了截距,所以两者都需要考虑,除此之外,我们还需要考虑的就是特殊情况,即斜率不存在的情况
所以我们就可以遍历所有的点,对于我们的任意两点是可以组成一条直线的,我们记录他的斜率和截距,最后我们再加上我们没有计算的斜率不存在的情况,斜率不存在的情况也很简单,就是我们的x坐标的范围,在这道题就是20。
代码
#直线
points=[[x,y] for x in range(20) for y in range(21)] #创建二维列表:代表xy坐标系
docker=set() #创建集合属性的容器:因为集合里的元素不会重复
for i in range(len(points)): #二重循环遍历每个坐标
x1,y1=points[i][0],points[i][1] #注意书写格式:a,b=c,d
for j in range(len(points)):
x2,y2=points[j][0],points[j][1]
if x1==x2: #特殊情况:直线垂直时斜率不存在,先跳过最后计算
continue
k=(y2-y1)/(x2-x1) #斜率公式
b=(x2*y1-x1*y2)/(x2-x1) #截距公式
if (k,b) not in docker: #存入容器里没有的(斜率,截距)对
docker.add((k,b))
print(len(docker)+20) #输出结果:容器的长度40237+斜率不存在的20种情况=40257
答案
40257
试题 C:货物摆放
思路
这道题说简单也简单,不简单也不简单,如果用穷举的方法,用python不知道要猴年马月才能算出来
所以我们就要用聪明一点的方法,比如我们可以首先得到我们的n的约数,之后来说,就是这些约数的组合了,因为我们肯定要保证长宽高是我们的n的约数的,要不然不可能可以满足题目条件
接着我们就可以对约数进行一个组合,如果组合结果相乘是n的话,我们就可以把方案输出
tips:其实对于这道题来说,我们还可以利用分解质因数的方法,这个方法就可以在1s内输出结果,简单来说,就是会对我们的n分解质因数,对于分解的质因数我们也可以进行组合变成我们的因数,最后结果相乘就是我们的n,因为都是分解质因数得来的结果。
代码
n=2021041820210418 #货物数量
ans=0 #初始统计值为0
docker=set() #创建集合属性的容器
for i in range(1,int(n**0.5)+1):#循环遍历,筛选n的约数(对n开根号是为了加快速度)
if n%i==0:
docker.add(i)
docker.add(n//i)
for i in docker: #三重循环遍历容器(放心,三重不慢,运行5s就出结果)
for j in docker:
for k in docker:
if i*j*k==n: #满足条件,方案数+1
ans+=1
print(ans) #输出结果:2430
答案
2430
试题 D:路径
思路
这道题还是比较简单得到,我们可以看到这个是一个无向图,我们可以直接用动态规划的思想,也就是DP的思想来解决,首先我们可以看到条件,两个节点,如果差的绝对值大于21,就无边先连,否则,就用长度为最小公倍数的无向边相连。
我们求的是最短路径,首先我们就可以遍历差为21的数,这里就是用二层循环表示,最后我们再用dp的方式,这个动态转移的方法还是简单的,两种情况,直接用当前的数,或者加上最小倍数的长度,这两者的最小值就是我们的最短路径的长度
代码
import math
# 最小公倍数模板
def lcm(a,b):
return a*b//math.gcd(a, b)# 利用内置函数
dp = [float('inf')]*(2021+1)
dp[1] = 0 # 节点从0开始
for i in range(1,2021+1): # 1 -> 2021
for j in range(i+1,i+21+1): # 差为21
if j > 2021:
break
dp[j] = min(dp[j],dp[i]+lcm(i, j)) # 动态转移方程
print(dp[2021])
答案
10266837
试题 E:回路计数
思路
这道题主要的思路就是状态压缩的动态规划,简称状压DP
简单来说,我们可以用二进制来代表我们的21个状态,加入我们有4栋教学楼,那么我们访问所有教学楼就是1111(二进制),初始状态是从第一个教学楼走的,也就是初始状态为1000,利用这样的方法,如果有n栋教学楼,我们就有1<<n种状态,这道题就是2的21次方个状态除此之外,我们还需要一个记录两个教学楼是否互通,就是判断两者的最大公因数是否为1
最后我们就可以用这道题的核心,也就是我们状态压缩DP,对于这个状压DP来说,我们的想法就是,对于状态i,i的二进制表示中为1的位置,表示走过了教学楼 j
我们会枚举每一个状态,对于每一位来说,我们都会判断状态i是否包含第j栋教学楼,如果包含的话,就会进行判断其他位,枚举所有可能从教学楼k走到教学楼j的情况,加入我们的状态i除去j后还包含j的话,我们就可以加入其中的方案
最后我们就可以得到所有的方案,不过要减去j状态为0时候的方案,因为我们一开始就是从0,也就是第一件教学楼出现的。
代码
import os
import sys
from math import gcd
n = 21
m = 1 << n # 一共有2的21次方个状态
dp = [[0]*n for i in range(m)] # dp[i][j]对于状态i,i的二进制表示中为1的位置 表示走过了教学楼j
load = [[False]*n for i in range(n)] # 存储i, j之间是否有路
for i in range(1,n + 1):
for j in range(1,n + 1):
if gcd(i, j) == 1:
load[i-1][j-1] = True
dp[1][0] = 1 # 表示初始状态,也就是第二个状态 0000...0001 第一位为1
for i in range(1,m): # 枚举每一个状态
for j in range(n): # 对每一位 判断状态i是否包含第j栋教学楼
if i >> j & 1: # 如果第j+1位为1
for k in range(n): # 判断其他位 枚举所有可能从教学楼k走到教学楼j的情况
if (i - (1<<j)) >> k & 1 and load[k][j]: # 判断状态i除去j后是否包含k
dp[i][j] += dp[i-(1<<j)][k]
print(sum(dp[m-1]) - dp[m-1][0])
答案
881012367360