A门牌制作

【问题描述】
小蓝要为一条街的住户制作门牌号。这条街一共有 2020 位住户,门牌号从 1 到 2020 编号。小蓝制作门牌的方法是先制作 0 到 9 这几个数字字符,最后根据需要将字符粘贴到门牌上,例如门牌 1017 需要依次粘贴字符 1、0、1、7,即需要 1 个字符 0,2 个字符 1,1 个字符 7。

请问要制作所有的 1 到 2020 号门牌,总共需要多少个字符 2?

答案:624

res = 0
for i in range(1, 2021):
for j in str(i):
if j == '2':
res += 1
print(res)
# 624

B寻找2020

【问题描述】
小蓝有一个数字矩阵,里面只包含数字 0 和 2。小蓝很喜欢 2020,他想找
到这个数字矩阵中有多少个 2020 。

小蓝只关注三种构成 2020 的方式:

  • 同一行里面连续四个字符从左到右构成 2020。
  • 同一列里面连续四个字符从上到下构成 2020。
  • 在一条从左上到右下的斜线上连续四个字符,从左上到右下构成 2020。

例如,对于下面的矩阵:
220000
000000
002202
000000
000022
002020
一共有 5 个 2020。其中 1 个是在同一行里的,1 个是在同一列里的,3 个
是斜线上的。

小蓝的矩阵比上面的矩阵要大,由于太大了,他只好将这个矩阵放在了一个文件里面,在试题目录下有一个文件 2020.txt,里面给出了小蓝的矩阵。
请帮助小蓝确定在他的矩阵中有多少个 2020。

txt附件

答案:16520

with open('./2020.txt', 'r', encoding='utf-8') as file:
text = file.readlines()

cnt = 0
for line in text:
index = line.find('2020')
while index != -1:
cnt += 1
index = line.find('2020', index+2)

for i in range(len(text[0])):
for j in range(len(text)-3):
if text[j][i] == '2' and text[j+1][i] == '0' and text[j+2][i] == '2' and text[j+3][i] == '0':
cnt += 1

for i in range(len(text)-3):
for j in range(len(text[0])-3):
if text[i][j] == '2' and text[i+1][j+1] == '0' and text[i+2][j+2] == '2' and text[i+3][j+3] == '0':
cnt += 1
print(cnt)
# 16520

C跑步锻炼

【问题描述】
小蓝每天都锻炼身体。正常情况下,小蓝每天跑 1 千米。如果某天是周一或者月初(1 日),为了激励自己,小蓝要跑 2 千米。如果同时是周一或月初,小蓝也是跑 2 千米。小蓝跑步已经坚持了很长时间,从 2000 年 1 月 1 日周六(含)到 2020 年10 月 1 日周四(含)。请问这段时间小蓝总共跑步多少千米?

答案:8879

months = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
year, month, day, week = 2000, 1, 1, 5
res = 0
while True:
if day > months[month]:
day -= months[month]
month += 1
if month > 12:
month -= 12
year += 1

if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
months[2] = 29
else:
months[2] = 28

if day == 1 or week % 7 == 0:
res += 2
else:
res += 1

if year == 2020 and month == 10 and day == 1:
break
day += 1
week += 1

print(res)
# 8879

D蛇形填数

【问题描述】
如下图所示,小明用从 1 开始的正整数“蛇形”填充无限大的矩阵。

1 2 6 7 15 …
3 5 8 14 …
4 9 13 …
10 12 …
11 …

容易看出矩阵第二行第二列中的数是 5。请你计算矩阵中第 20 行第 20 列的数是多少?

答案:761

matrix = [[0]*50 for i in range(50)]
cnt = 0
i, j = 0, 0
di, dj = 0, 0
while matrix[19][19] == 0:
cnt += 1
i += di
j += dj
matrix[i][j] = cnt
if i == 0:
cnt += 1
j += 1
di = 1
dj = -1
matrix[i][j] = cnt
elif j == 0:
cnt += 1
i += 1
di = -1
dj = 1
matrix[i][j] = cnt

print(matrix[19][19])

E排序

【问题描述】
小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。
在冒泡排序中,每次只能交换相邻的两个元素。
小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符,则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。

例如,对于字符串 lan 排序,只需要 1 次交换。对于字符串 qiao 排序,总共需要 4 次交换。小蓝找到了很多字符串试图排序,他恰巧碰到一个字符串,需要 100 次交换,可是他忘了把这个字符串记下来,现在找不到了。请帮助小蓝找一个只包含小写英文字母且没有字母重复出现的字符串,对该串的字符排序,正好需要 100 次交换。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个。请注意字符串中可以包含相同的字符。

答案:jonmlkihgfedcba

思路:

我们知道,像ba,cba,dcba……这样逆序的字符串进行冒泡排序所需要的交换次数是在相同长度的字符串中最多的。而要求字典序尽量小,也就是我们要用字母表中尽量靠前的字母。所以可以依次尝试ba,cba,dcba……直到找到一个字符串冒泡排序次数刚好大于100的。尝试发现onmlkjihgfedcba所需交换次数为105,就是我们所需要的,也就是说我们要找的交换次数为100的字符串就是用这些字母组成的。 105大于100,所以需要减少交换次数,同时字典序要小,也就是把小字母往前放,依次尝试把a,b,c……放开头,直到j在开头时交换次数正好为100。如果调整j后面的字符只会让交换次数更小,所以这就是答案。

代码如下:

def bubble(t):
s = t.copy()
cnt = 0
for i in range(len(s)-1):
for j in range(len(s)-1-i):
if s[j] > s[j+1]:
s[j], s[j+1] = s[j+1], s[j]
cnt += 1
return cnt


s = []
for i in range(26):
s.insert(0, chr(97+i))
cnt = bubble(s)
if cnt >= 100:
break

for i in range(2, len(s)-1):
tmp = list(s[-i])+s[:-i]+s[-i+1:]
if bubble(tmp) >= 100:
print(''.join(tmp))
print(bubble(tmp))
break

F成绩统计

【问题描述】
小蓝给学生们组织了一场考试,卷面总分为 100 分,每个学生的得分都是一个 0 到 100 的整数。
如果得分至少是 60 分,则称为及格。如果得分至少为 85 分,则称为优秀。请计算及格率和优秀率,用百分数表示,百分号前的部分四舍五入保留整数。

【输入格式】
输入的第一行包含一个整数 n,表示考试人数。
接下来 n 行,每行包含一个 0 至 100 的整数,表示一个学生的得分。

【输出格式】
输出两行,每行一个百分数,分别表示及格率和优秀率。百分号前的部分
四舍五入保留整数。

【样例输入】
7
80
92
56
74
88
100
0

【样例输出】
71%
43%

n = int(input())
great = 0
pas = 0
for i in range(n):
num = int(input())
if num >= 85:
great += 1
if num >= 60:
pas += 1
print("{:d}%".format(round(100*pas/n)))
print("{:d}%".format(round(100*great/n)))

G单词分析

【问题描述】
小蓝正在学习一门神奇的语言,这门语言中的单词都是由小写英文字母组成,有些单词很长,远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词,他准备不再完全记忆这些单词,而是根据单词中哪个字母出现得最多来分辨单词。现在,请你帮助小蓝,给了一个单词后,帮助他找到出现最多的字母和这个字母出现的次数。

【输入格式】
输入一行包含一个单词,单词只由小写英文字母组成。

【输出格式】
输出两行,第一行包含一个英文字母,表示单词中出现得最多的字母是哪
个。如果有多个字母出现的次数相等,输出字典序最小的那个。
第二行包含一个整数,表示出现得最多的那个字母在单词中出现的次数。

【样例输入】
lanqiao

【样例输出】
a
2

【样例输入】
longlonglongistoolong

【样例输出】
o
6

word = input()
d = {}
for i in word:
d[i] = d.get(i, 0)+1
dic = sorted(list(d.items()))
dic.sort(key=lambda x: x[1], reverse=True)
print(dic[0][0])
print(dic[0][1])

H数字三角形

【问题描述】

1

上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。
路径上的每一步只能从一个数走到下一层和它最近的左边的那个数或者右边的那个数。此外,向左下走的次数与向右下走的次数相差不能超过 1。

【输入格式】
输入的第一行包含一个整数 N (1 < N ≤ 100),表示三角形的行数。下面的N 行给出数字三角形。数字三角形上的数都是 0 至 100 之间的整数。

【输出格式】
输出一个整数,表示答案。

【样例输入】
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

【样例输出】
27

思路:

基础动态规划加了一个限制:向左下走的次数与向右下走的次数相差不能超过 1。我们可以在常规步骤基础上再加两个数组left和right用于记录走到最后一行时向左下走的次数和向右下走的次数,在向下扩展时进行维护即可。只要最后遍历最后一行找一个最大的和数并且left[i]和right[i]相差不超过1.

代码如下:

from math import *

n = int(input())
res, left, right = [0]*n, [0], [0]
for i in range(n):
line = list(map(int, input().split()))
if i == 0:
res[0] = line[0]
else:
left0, right0 = [0]*len(line), [0]*len(line)

for j in range(len(line)):
if j == 0:
line[0] += res[0]
left0[0] = left[0]+1
right0[0] = right[0]
elif j == len(line)-1:
line[j] += res[j-1]
left0[j] = left[j-1]
right0[j] = right[j-1]+1
else:
if res[j-1] > res[j]:
line[j] += res[j-1]
left0[j] = left[j-1]
right0[j] = right[j-1]+1
else:
line[j] += res[j]
left0[j] = left[j]+1
right0[j] = right[j]
left = left0.copy()
right = right0.copy()

res = line.copy()


maxn = 0
for i in range(n):
if maxn < res[i] and abs(left[i]-right[i]) <= 1:
maxn = res[i]
print(maxn)

I平面切分

【问题描述】
平面上有 N 条直线,其中第 i 条直线是 y = Ai · x + Bi。
请计算这些直线将平面分成了几个部分。

【输入格式】
第一行包含一个整数 N。
以下 N 行,每行包含两个整数 Ai, Bi。

【输出格式】
一个整数代表答案。

【样例输入】
3
1 1
2 2
3 3

【样例输出】
6

【评测用例规模与约定】
对于 50% 的评测用例,1 ≤ N ≤ 4, 10 ≤ Ai, Bi ≤ 10。
对于所有评测用例,1 ≤ N ≤ 1000, 100000 ≤ Ai, Bi ≤ 100000。

骗分技巧:样例输入输出可以用if判断来处理,大概能过一组数据,然后就是所有直线都平行的特殊情况,输出n+1,又可以骗一组

解题思路:
我们知道没有直线的时候,平面只有一个
当有一条直线的时候,平面被分成两部分

有两条直线的时候,这时有三种情况:平行,重合,相交
平行的话平面分成三部分(没有不同的交点)
重合的话平面分成两部分(无意义的一步)
相交的话平面分成四部分(有一个不同的交点)

有三条直线的时候,新加入的直线和之前的两条直线也有三种情况:平行 重合 相交
和前面所有直线平行的话平面在之前基础上划分平面数量加1(没有交点)
重合(无意义,等于啥也没干)
相交的话
如果和两者都于相交一个点(之前基础基础上数量加1+1)
如果和两者相交不同的两个点(之前基础基础上数量加2+1)

规律:每次新加入的线段如果和之前的线段有n个不同的交点,就在原来的基础上多分出n+1个平面

所以只要考虑和前面有几个不同的交点

代码如下:

def getpoint(a1, b1, a2, b2):
if a1 == a2:
return -1
x = (b2 - b1)/(a1 - a2)
y = (a1 * b2 - a2 * b1) / (a1 - a2)
return x, y


n = int(input())
tmp = [tuple(map(int, input().split())) for i in range(n)]
line = list(set(tmp)) # 用set去重之后转list便于遍历
res = 2
for i in range(1, len(line)):
point = set()
for j in range(i):
p = getpoint(line[j][0], line[j][1], line[i][0], line[i][1])
if p != -1:
point.add(p)
res += len(point)+1
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 件装备能得到的总价值达到最大。

【输入格式】
输入的第 1 至 6 行,包含 6 件装备的描述。其中第 i 行的第一个整数 Ni 表示第 i 件装备的装饰孔数量。后面紧接着 Ni 个整数,分别表示该装备上每个装饰孔的等级 L(1 ≤ L ≤ 4)。
第 7 行包含一个正整数 M,表示装饰珠 (技能) 种类数量。
第 8 至 M + 7 行,每行描述一种装饰珠 (技能) 的情况。每行的前两个整数Lj(1 ≤ Lj ≤ 4) 和 Pj(1 ≤ Pj ≤ 7) 分别表示第 j 种装饰珠的等级和上限。接下来Pj 个整数,其中第 k 个数表示装备该种装饰珠数量为 k 时的价值 Wj(k)。

【输出格式】
输出一行包含一个整数,表示能够得到的最大价值。

【样例输入】
1 1
2 1 2
1 1
2 2 2
1 1
1 3
3
1 5 1 2 3 5 8
2 4 2 4 8 15
3 2 5 10

【样例输出】
20

【样例说明】
按照如下方式镶嵌珠子得到最大价值 18,括号内表示镶嵌的装饰珠的种类
编号:
1: (1)
2: (1) (2)
3: (1)
4: (2) (2)
5: (1)
6: (2)
4 颗技能 1 装饰珠,4 颗技能 2 装饰珠 W1(4) + W2(4) = 5 + 15 = 20。

【评测用例规模与约定】
对于 30% 的评测用例,1 ≤ Ni ≤ 10, 1 ≤ M ≤ 20, 1 ≤ Wj(k) ≤ 500;
对于所有评测用例,1 ≤ Ni ≤ 50, 1 ≤ M ≤ 10000, 1 ≤ Wj(k) ≤ 10000。