第十一届省赛真题
一,门牌制作(A)
小蓝要为一条街的住户制作门牌号。
这条街一共有 2020 位住户,门牌号从 1到 2020 编号。
小蓝制作门牌的方法是先制作 0到9 这几个数字字符,最后根据需要将字符粘贴到门牌上,例如门牌 1017 需要依次粘贴字符1、0、1、7,即需要 1 个字符 0,2 个字符 1,1 个字符 7。
请问要制作所有的 1 到 2020 号门牌,总共需要多少个字符 2?
---
解题思路:
遍历1-2020的数字,如果数字中有2,则将数字中2的个数追加到列表中,最后求和列表中所有的数即可
这里应该注意的是find函数在找不到时,会返回-1,所以if条件判断应该是不等于-1
result = []
for i in range(1, 2021):
i = str(i)
if i.find('2') != -1:
result.append(i.count('2'))
print(sum(result))
二,寻找2020(B)
小蓝有一个数字矩阵,里面只包含数字 0和 2。小蓝很喜欢 2020,他想找到这个数字矩阵中有多少个 2020 。小蓝只关注三种构成 2020 的方式:
1.同一行里面连续四个字符从左到右构成 2020。
2.同一列里面连续四个字符从上到下构成 2020.
3.在一条从左上到右下的斜线上连续四个字符,从左上到右下构成 2020
例如,对于下面的矩阵
一共有5个 2020。其中1个是在同一行里的,1个是在同一列里的3 个是斜线上的。
小蓝的矩阵比上面的矩阵要大,如下给出了小蓝的矩阵(300*300)。
请帮助小蓝确定在他的矩阵中有多少个 2020。
---
解题思路:
用列表按行存储,按行查找就是每个元素内查找,按列查找就是list[i]j
对角线查找则i,j都改变
按行存储的实现是靠split方法实现的,去除两边的空格获得每一行的字符串形式,将其追加到列表中即可
查找2020时值得注意的是,不能用count函数直接统计数量,例如‘202020’中包含有两个2020,但是使用count只能获得数量1。
count = 0
num_list = []
for _ in range(300):
row = input().strip()
num_list.append(row)
j = 0
# 寻找在一行的2020
for i in num_list:
i = ''.join(i)
if i.find('2020') != -1:
for x in range(len(i) - 3):
if i[x:x + 4] == '2020':
count += 1
# 寻找列中的2020
while j < 300:
col = ''
for i in range(300):
col += num_list[i][j]
if col.find('2020') != -1:
for x in range(len(col) - 3):
if col[x:x + 4] == '2020':
count += 1
j += 1
# 寻找对角线(左上到右下——对角线上半部分)的2020
for i in range(300):
result = ''
for j in range(300-i):
result += num_list[j][i+j]
if result.find('2020') != -1:
for x in range(len(result) - 3):
if result[x:x + 4] == '2020':
count += 1
# 寻找对角线(左上到右下_对角线下半部分)的2020
for i in range(300,0,-1):
result = ''
for j in range(300-i):
result += num_list[i+j][j]
if result.find('2020') != -1:
for x in range(len(result) - 3):
if result[x:x + 4] == '2020':
count += 1
print(count)
优化后的代码:
num_list = [input().strip() for _ in range(300)]
count = 0
# 检查行
for row in num_list:
count += sum(1 for i in range(len(row) - 3) if row[i:i+4] == '2020')
# 检查列
for j in range(300):
col = ''.join(num_list[i][j] for i in range(300))
count += sum(1 for i in range(len(col) - 3) if col[i:i+4] == '2020')
# 检查左上到右下的对角线
for i in range(300):
diagonal1 = ''.join(num_list[j][i + j] for j in range(300 - i))
count += sum(1 for i in range(len(diagonal1) - 3) if diagonal1[i:i+4] == '2020')
# 检查左下到右上的对角线
for i in range(1, 300):
diagonal2 = ''.join(num_list[i + j][j] for j in range(300 - i))
count += sum(1 for i in range(len(diagonal2) - 3) if diagonal2[i:i+4] == '2020')
print(count)
三,跑步锻炼(C)
小蓝每天都锻炼身体,正常情况下,小蓝每天跑 1千米。如果某天是周一或者月初(1 日),为了激励自己,小蓝要跑 2千米。如果同时是周一或月初,小蓝也是跑 2 千米。
小蓝跑步已经坚持了很长时间,从2000年1月1日周六(含)到 2020年10月1日周四(含)。请问这段时间小蓝总共跑步多少干米?
---
解题思路:
使用datetime模块,datetime模块获取的是一个datetime对象。
将起始和终止日期存入变量,也必须创建一个时间间隔的变量用于递增
最终通过循环+条件判断即可解题
import datetime
start = datetime.date(2000, 1, 1)
end = datetime.date(2020, 10, 1)
days = datetime.timedelta(days=1)
ans = 0
while end >= start:
if start.day == 1 or start.weekday() == 0:
ans += 2
else:
ans += 1
start += days
print(ans)
四,蛇形填数(D)
如下所示,小明用从1开始的正整数“蛇形”填充无限大的矩阵。
容易看出矩阵第二行第二列中的数是 5。请你计算矩阵中第 20行第 20 列的数是多少?
---
解题思路:
很明显是一个数学找规律的题,
第n行n列=n*n+(n-1)*(n1)
所以答案是20*20+19*19=761
五,字串排序(E)
小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。在冒泡排序中,每次只能交换相邻的两个元素
小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。
例如,对于字符串 lan 排序,只需要1次交换。对于字符串 qiao 排序,总共需要 4 次交换。
小蓝找到了很多字符串试图排序,他恰巧碰到一个字符串,需要 100 次交换,可是他忘了吧这个字符串记下来,现在找不到了。
请帮助小蓝找一个只包含小写英文字母且没有字母重复出现的字符串,对该串的字符排序,正好需要 100 次交换。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个。
---
解题思路:
没理解,待做
六,成绩统计(F)
小蓝给学生们组织了一场考试,卷面总分为 100分,每个学生的得分都是一个 0到 100 的整数。
如果得分至少是 60 分,则称为及格。如果得分至少为 85分,则称为优秀。
请计算及格率和优秀率,用百分数表示,百分号前的部分四舍五入保留整数。
---
解题思路:
将数据的成绩转换为列表后循环判断即可
round()用于数字的格式化,rounf(num1[,num2])其中num1是被格式化的数字,num2是需要保留的小数位数。
sum_num = int(input())
pass_stu = 0
good_stu = 0
for _ in range(sum_num):
score = int(input().strip())
if score >= 60:
pass_stu += 1
if score >= 85:
good_stu += 1
pass_percentage = round(pass_stu / sum_num * 100)
good_percentage = round(good_stu / sum_num * 100)
print(str(pass_percentage) + "%")
print(str(good_percentage) + "%")
七,单词分析(G)
小蓝正在学习一门神奇的语言,这门语言中的单词都是由小写英文字母组 成,有些单词很长,远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词,他准备不再完全记忆这些单词,而是根据单词中哪个字母出现得最多来分辨单词。
现在,请你帮助小蓝,给了一个单词后,帮助他找到出现最多的字母和这 个字母出现的次数。
---
解题思路:
str_num = []
end_str = []
mystr = input()
for i in mystr:
str_num.append(mystr.count(i))
for i in mystr:
if max(str_num) == mystr.count(i):
end_str.append(i)
print(min(end_str))
print(max(str_num))
优化版:
mystr = input()
max_count = 0
most_common = []
for char in mystr:
count = mystr.count(char)
if count > max_count:
max_count = count
most_common = [char]
elif count == max_count and char not in most_common:
most_common.append(char)
print(min(most_common))
print(max_count)
八,数字三角形(H)
上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。
路径上的每一步只能从一个数走到下一层和它最近的左边的那个数或者右 边的那个数。此外,向左下走的次数与向右下走的次数相差不能超过 1。
---
解题思路:
# 动态规划
这题看了题解才勉强看懂,把题解的代码放在下面了:
根据题解的意思来看,每一行的路径最大数都可以通过相邻上一行的数字取最大值得到。
最左边与最右边不需要取最大值,直接与上一行的最左/最右相加即可
因为题目要求向左和向右的次数不能相差超过1,所以结果不是在最后一行直接找最大值,而是:
奇数行直接取最后一行的最中间,偶数行取中间两个的大值
这里是因为:已知奇数行会移动偶数次,偶数行会移动奇数次
那么要求差不能超过1就代表着奇数的左右移动次数是相等的,即符合条件的最大路径数在中间。
偶数行的也只会在中间两个的位置。
有意思的是这个题解用来判断是否奇数用的是&来判断,因为1的二进制位数最后一位为1,其他为0。奇数的二进制最后一位也肯定是1。
h = int(input()) # 输入数据
W = [list(map(int, input().split())) for i in range(h)]
# 循环遍历计算到每一行的和的最大值
for i in range(1, h):
for j in range(0, i + 1):
if j == 0: # 最左边元素只能由右上方得到
W[i][j] += W[i - 1][j]
elif j == i: # 最右边元素只能由左上方得到
W[i][j] += W[i - 1][j - 1]
else: # 其余元素由上方较大值得到
W[i][j] += max(W[i - 1][j - 1: j + 1])
if h & 1: # 如果是奇数行,则返回最中间值
print(W[-1][h // 2])
else: # 偶数行则返回中间较大值
print(max(W[-1][h // 2 - 1], W[-1][h // 2]))
九,平面切分(I)
平面上有 N 条直线,其中第i条直线是 y = Ai * x+ Bi
请计算这些直线将平面分成了几个部分
---
解题思路:
这个题主要在于数学分析,想不明白,下面是题解
总体思路就是题解中注释前三行中写的,根据判断直线相交的点数以及直线个数来计算最终区域个数
难度不在于程序编写,而是数学分析计算
# 初始平面数为1
# 平面中每添加一条直线,平面数+1
# 新添加的直线与已有的直线的交点个数为N, 平面数再+N
def isintersect(k1, b1, k2, b2):
# 判断两条直线是否相交
if k1 != k2:
return True
else:
return False
def getpoint(k1, b1, k2, b2):
# 计算两条直线的交点
x = (b2 - b1) / (k1 - k2)
y = (k2 * b1 - k1 * b2) / (k2 - k1)
return (x, y)
def isin(x0, y0, k, b):
# 判断某点是否在某条直线上
if y0 == k * x0 + b:
return True
else:
return False
N = int(input())
li = [list(map(int, input().split())) for i in range(N)]
exist = [] # 记录平面中已有的直线
# exist.append(li[0])
res = 1 # 记录平面的个数,初始为1
for item in li:
points= [] # 记录新添加的直线与已有直线的交点
if item in exist: # 如果已有这条直线则直接跳过
continue
for e in exist: # 遍历已有的直线
if isintersect(item[0], item[1], e[0], e[1]):
# 如果新添加的直线与已有的直线相交
# 计算它们的交点并记录
new = getpoint(item[0], item[1], e[0], e[1])
if new not in points:
points.append(new)
res += (len(points) + 1)
exist.append(item)
print(res)
十,装饰珠(J)
在怪物猎人这一款游戏中,玩家可以通过给装备镶嵌不同的装饰珠来获取相应的技能,以提升自己的战斗能力。
已知猎人身上一共有 6件装备,每件装备可能有若干个装饰孔,每个装饰孔有各自的等级,可以镶嵌一颗小于等于自身等级的装饰珠(也可以选择不镶嵌)。装饰珠有M种,编号1至M,分别对应M种技能,第i种装饰珠的等级为 Li,只能镶嵌在等级大于等于 Li的装饰孔中。
对第 i种技能来说,当装备相应技能的装饰珠数量达到 Ki个时,会产生 Wi(Ki)
的价值。镶嵌同类技能的数量越多,产生的价值越大,即 Wi(Ki−1)<Wi(Ki)。但每个技能都有上限 Pi(1≤Pi≤7),当装备的珠子数量超过 Pi时,只会产生 Wi(Pi)的价值。
对于给定的装备和装饰珠数据,求解如何镶嵌装饰珠,使得 6件装备能得到的总价值达到最大。
---
解题思路:
没太懂题目,待做