前言
20240413更新,刚打完,属于是菜鸟写算法。
试题 A: 拼正方形
【问题描述】 小蓝正在玩拼图游戏,他有 7385137888721 个 2 × 2 的方块和 10470245 个 1 × 1 的方块,他需要从中挑出一些来拼出一个正方形,比如用 3 个 2 × 2 和 4 个 1 × 1 的方块可以拼出一个 4 × 4 的正方形,用 9 个 2 × 2 的方块可以拼出一 个 6 × 6 的正方形,请问小蓝能拼成的最大的正方形的边长为多少。
【答案提交】 这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
思路:题目挺小学奥数的,简单思考一下即可。考场我先计算了一下总面积开方:
import math
a = 7385137888721
b = 10470245
print(math.sqrt(a*4+b))
结果输出为整数,那答案就是它了。
答案:5435123
试题 B: 召唤数学精灵
【问题描述】 数学家们发现了两种用于召唤强大的数学精灵的仪式,这两种仪式分别被 称为累加法仪式 A(n) 和累乘法仪式 B(n)。 累加法仪式 A(n) 是将从 1 到 n 的所有数字进行累加求和,即:A(n) = 1 + 2 + · · · + n 。 累乘法仪式 B(n) 则是将从 1 到 n 的所有数字进行累乘求积,即:B(n) = 1 × 2 × · · · × n 。 据说,当某个数字 i 满足 A(i) − B(i) 能被 100 整除时,数学精灵就会被召 唤出来。 现在,请你寻找在 1 到 2024041331404202 之间有多少个数字 i,能够成功 召唤出强大的数学精灵。
【答案提交】 这是一道结果填空题,你只需要算出结果后提交即可。本题的结果为一个 整数,在提交答案时只填写这个整数,填写居中对齐多余的内容将无法得分。
我召唤了20+分钟,反正我没召唤出来,最后,随便填写了一个数字。
试题 C: 数字诗意
时间限制: 10.0s 内存限制: 512.0MB 本题总分:10 分
【问题描述】 在诗人的眼中,数字是生活的韵律,也是诗意的表达。 小蓝,当代顶级诗人与数学家,被赋予了 “数学诗人” 的美誉。他擅长将冰 冷的数字与抽象的诗意相融合,并用优雅的文字将数学之美展现于纸上。 某日,小蓝静坐书桌前,目光所及,展现着 n 个数字,它们依次为 a1, a2, . . . , an,熠熠生辉。小蓝悟到,如果一个数能够以若干个(至少两个) 连续的正整数相加表示,那么它就蕴含诗意。例如,数字 6 就蕴含诗意,因为 它可以表示为 1 + 2 + 3。而 8 则缺乏诗意,因为它无法用连续的正整数相加表 示。 小蓝希望他面前的所有数字都蕴含诗意,为此,他决定从这 n 个数字中删 除一部分。请问,小蓝需要删除多少个数字,才能使剩下的数字全部蕴含诗意?
【输入格式】 输入的第一行包含一个整数 n,表示展示的数字个数。 第二行包含 n 个整数 a1, a2, . . . , an,相邻整数之间使用一个空格分隔,表示 展示的数字。
【输出格式】 输出一行包含一个整数,表示小蓝需要删除的数字个数,以使剩下的数字 全部蕴含诗意。
【样例输入】
3
3 6 8
【样例输出】 1
【样例说明】 在样例中,数字 3 可以表示为 1 + 2,数字 6 可以表示为 1 + 2 + 3,数字 8 无法表示为连续的正整数相加,因此,需要删除的数字个数为 1。
【评测用例规模与约定】
对于 30% 的评测用例,1 ≤ n ≤ 10^3,1 ≤ ai ≤ 10^3。
对于所有评测用例,1 ≤ n ≤ 2 × 10^5,1 ≤ ai ≤ 10^16。
思路:想不出来好的思路,直接打表了。对于n=1000这部分的分数死活能拿到手。
n = int(input())
q = list(map(int, input().split(" ")))
table = []
# 打表
table_len = 300
for i in range(1, table_len):
for j in range(i + 2, table_len):
t = 0
for k in range(i, j):
t += k
table.append(t)
table = set(table)
res = 0
for i in q:
if i not in table:
res += 1
print(res)
试题 D: 回文数组
时间限制: 10.0s 内存限制: 512.0MB 本题总分:10 分
【问题描述】 小蓝在无聊时随机生成了一个长度为 n 的整数数组,数组中的第 i 个数为 ai,他觉得随机生成的数组不太美观,想把它变成回文数组,也是就对于任意 i ∈ [1, n] 满足 ai = an−i+1 。小蓝一次操作可以指定相邻的两个数,将它们一起加 1 或减 1 ;也可以只指定一个数加 1 或减 1 ,请问他最少需要操作多少次能把 这个数组变成回文数组?
【输入格式】 输入的第一行包含一个正整数 n 。 第二行包含 n 个整数 a1, a2, · · · , an ,相邻整数之间使用一个空格分隔。
【输出格式】 输出一行包含一个整数表示答案。
【样例输入】
4
1 2 3 4
【样例输出】 3
【样例说明】
第一次操作将 a1, a2 加 1 ,变为 2, 3, 3, 4 ;
后面两次操作将 a1 加 1 ,变为 4, 3, 3, 4 。
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n ≤ 10;
对于所有评测用例,1 ≤ n ≤ 10^5 ,−10^6 ≤ ai ≤ 10^6 。
思路:一开始想着区间差分数组的解法来着,感觉很简单,不过这只能改相邻的两个的条件,我死活没想出好的解法,然后暴力了。
暴力的思路也很简单,遍历列表,每次看看左边有没有一起带走的数,然后递归,代码没怎么优化,常数估计很大,估计只能拿20%的分。
代码:
from math import inf
def sgn(n, ln):
if n > 0 and ln > 0:
return "+"
elif n < 0 and ln < 0:
return "-"
else:
return "None"
def dfs(pivot):
if pivot >= len(lst):
return
global res, final
n, ln = lst[pivot], lst[pivot - 1]
final = min(final, res + sum(map(abs, lst)))
if sgn(n, ln) == "+": # 同号
min_t = min(n, ln)
lst[pivot] -= min_t
lst[pivot - 1] -= min_t
res += min_t
final = min(final, res + sum(map(abs, lst)))
dfs(pivot + 1) # 递归
# 还原现场
lst[pivot] += min_t
lst[pivot - 1] += min_t
res -= min_t
elif sgn(n, ln) == "-":
max_t = max(n, ln)
lst[pivot] -= max_t
lst[pivot - 1] -= max_t
res -= max_t
final = min(final, res + sum(map(abs, lst)))
dfs(pivot + 1) # 递归
# 还原现场
lst[pivot] += max_t
lst[pivot - 1] += max_t
res += max_t
# print(lst)
dfs(pivot + 1) # 递归
n = int(input())
lst = list(map(int, input().split(" ")))
# 数据预处理
first = []
second = []
if n % 2 == 0: # 偶数
first = lst[:n // 2]
second = lst[n // 2:]
else:
first = lst[:n // 2]
second = lst[n // 2 + 1:]
second.reverse()
for i in range(len(first)):
first[i] = first[i] - second[i]
lst = first
# 下面进行暴搜,差分的方法想不出来,我不确定差分能不能求解
res = 0
final = inf
dfs(0)
print(final)
试题 E: 吊坠
时间限制: 10.0s 内存限制: 512.0MB 本题总分:15 分
【问题描述】 小蓝想制作一个吊坠,他手上有 n 个长度为 m 的首尾相连的环形字符串 {s1, s2, · · · , sn} ,他想用 n − 1 条边将这 n 个字符串连接起来做成吊坠,要求所 有的字符串连完后形成一个整体。连接两个字符串 si , sj 的边的边权为这两个字 符串的最长公共子串的长度(可以按环形旋转改变起始位置,但不能翻转),小 蓝希望连完后的这 n − 1 条边的边权和最大,这样的吊坠他觉得最好看,请计算 最大的边权和是多少。
【输入格式】 输入的第一行包含两个正整数 n, m ,用一个空格分隔。 接下来 n 行,每行包含一个长度为 m 的字符串,分别表示 s1, s2, · · · , sn 。
【输出格式】 输出一行包含一个整数表示答案。
【样例输入】
4 4
aabb
abba
acca
abcd
【样例输出】
8
【样例说明】
连接 < 1, 2 >, < 2, 3 >, < 2, 4 > ,边权和为 4 + 2 + 2 = 8
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n, m ≤ 10 ;
对于所有评测用例,1 ≤ n ≤ 200 ,1 ≤ m ≤ 50 。所有字符串由小写英文字 母组成。
思路:几个常见的算法结合起来即可:
1.最长公共子序列(dp解决)
2.最小生成树(该题要改成最大生成树)
3.最小生成树的并查集
题目有个麻烦就是,环形旋转,要处理一下。
代码:
def find(parent, vertex):
if parent[vertex] == vertex:
return vertex
return find(parent, parent[vertex])
def union(parent, rank, vertex1, vertex2):
root1 = find(parent, vertex1)
root2 = find(parent, vertex2)
if root1 != root2:
if rank[root1] > rank[root2]:
parent[root2] = root1
else:
parent[root1] = root2
if rank[root1] == rank[root2]:
rank[root2] += 1
def kruskal_algorithm(graph):
# 初始化结果
minimum_spanning_tree = []
# 初始化并查集
parent = {vertex: vertex for vertex in graph.keys()}
rank = {vertex: 0 for vertex in graph.keys()}
# 获取所有的边
edges = []
for vertex, neighbors in graph.items():
for neighbor, weight in neighbors.items():
edges.append((vertex, neighbor, weight))
# 按权值排序边
edges.sort(key=lambda edge: edge[2], reverse=True) # 反转取最大的边
# 不断取出权值最小的边并判断是否形成环
for edge in edges:
vertex1, vertex2, weight = edge
if find(parent, vertex1) != find(parent, vertex2):
union(parent, rank, vertex1, vertex2)
minimum_spanning_tree.append(edge)
if len(minimum_spanning_tree) == len(graph) - 1:
break
return minimum_spanning_tree
# 动态规划求解,存储解及解的计算过程
def lcs(x, y): # 求解并存储箭头方向,x,y为字符串、列表等序列
m = len(x) # x的长度
n = len(y) # y的长度
c = [[0 for i in range(n + 1)] for _ in range(m + 1)] # 二维数组,初始值为0,用于存储长度结果
d = [[0 for i in range(n + 1)] for _ in range(m + 1)] # 二维数组,初始值为0,用于存储箭头方向,1表示左上,2表示上,3表示左
for i in range(1, m + 1): # 按行遍历二维数组
for j in range(1, n + 1): # 每行的各数值遍历, c0j和ci0相关的值都为0,所以均从1开始
if x[i - 1] == y[j - 1]: # xi=yi的情况,二维数组中i,j=0时,都为0已经确定,但字符串x,y仍需从0开始遍历
c[i][j] = c[i - 1][j - 1] + 1 # 递推式
d[i][j] = 1 # 箭头方向左上方
elif c[i][j - 1] > c[i - 1][j]: # 递推式,选择更大的
c[i][j] = c[i][j - 1]
d[i][j] = 3 # 箭头左边
else: # c[i-1][j] >= c[i][j-1]
c[i][j] = c[i - 1][j]
d[i][j] = 2 # 箭头上方
return c[m][n], d
def longest_dis(i, j):
a, b = lst[i], lst[j]
res = 0
for i in range(len(a)):
t = a[i:] + a[:i]
c, d = lcs(t, b)
res = max(c, res)
return res
# 求解距离矩阵
def distance():
dis = dict()
for i in range(1, n + 1):
dis[lst[i]] = {}
for i in range(1, n + 1):
for j in range(i, n + 1):
tt = longest_dis(i, j)
dis[lst[i]][lst[j]] = tt
dis[lst[j]][lst[i]] = tt
return dis
n,m = map(int,input().split())
lst = ["#"] + [input() for _ in range(n)]
dis = distance() # 获得距离矩阵
p = kruskal_algorithm(dis) # 求最大生成树
res = 0
for a, b, v in p:
res += v
print(res)
有些地方复杂度写得很高,希望多过点样例吧!
试题 F: 砍柴
时间限制: 10.0s 内存限制: 512.0MB 本题总分:15 分
【问题描述】
小蓝和小乔正在森林里砍柴,它们有 T 根长度分别为 n1, n2, · · · , nT 的木 头。对于每个初始长度为 n 的木头,小蓝和小乔准备进行交替砍柴,小蓝先出 手。每次砍柴时,若当前木头长度为 x ,需要砍下一截长度为 p 的木头,然后 换另一个人继续砍,其中 2 ≤ p ≤ x 且 p 必须为质数。当轮到某一方时 x = 1 或 x = 0 ,它就没法继续砍柴,它就输了。它们会使用最优策略进行砍柴。请对每 根木头判断是小蓝赢还是小乔赢,如果小蓝赢请输出 1 (数字 1),如果小乔赢 请输出 0 (数字 0)。
【输入格式】
输入的第一行包含一个正整数 T, 接下来 T 行,每行包含一个正整数,其中第 i 的整数为 ni 。
【输出格式】
输出 T 行,每行包含一个整数,依次表示对于每一根木头的答案。
【样例输入】
3
1
2
6
【样例输出】
0
1
1
【样例说明】
对于 n1 = 1 ,由于当前长度 x = 1 ,小蓝直接输掉,小乔赢; 对于 n2 = 2 ,小蓝选择 p = 2 ,轮到小乔时当前长度 x = 2 − 2 = 0 ,小乔 输掉,小蓝赢; 对于 n3 = 6 ,小蓝选择 p = 5 ,轮到小乔时 x = 6 − 5 = 1 ,小乔输掉,小 蓝赢。
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ ni ≤ 10^3;
对于所有评测用例,1 ≤ ni ≤ 10^5 ,1 ≤ T ≤ 10^4。
思路:这题代码倒不是很难很难,属于可以写的回溯(dfs),但是我题目看半天,只能说我对这种要文字理解的题目脑子容易抽筋,主要是它样例就给最简单的,复杂的不给,要我自己推,可恶!!!
代码注释给的思路我觉得是清楚的。
代码:
import math
def is_prime(x):
if x <= 3:
return True
for i in range(2, int(math.sqrt(x)) + 1):
if x % i == 0:
return False
return True
def solve(x, turn): # 小兰的回合则为False,不是为True
if x == 0 or x == 1:
return turn # 是小兰的回合则为输了,反之则赢了
if x != 0 and x != 1 and is_prime(x):
return not turn # 是小兰的回合则为赢了,反之则输了
# 下面进行暴搜
flag = False
for i in range(x - 1, 1, -1):
if is_prime(i):
# print("turn={}".format(turn))
# print("i={}".format(i))
flag = solve(x - i, not turn)
if flag: # 有赢的机会
return True # 终止
return False # 尝试所有机会,没有赢的可能
n = int(input())
lst = []
for i in range(n):
lst.append(int(input()))
res = []
for i in range(n):
t = solve(lst[i], False) # 0表示小蓝回合,1表示小乔回合
if t:
res.append(1)
else:
res.append(0)
for i in range(n):
print(res[i])
试题 G: 智力测试
时间限制: 10.0s 内存限制: 512.0MB 本题总分:20 分
【问题描述】
小蓝考上了世界上最好的魔法师学校,然而入学第一件事就是智力测试, 老师给出了一个 n × m 大小的棋盘,同时对每行每列设置了权重 {R1, R2, · · · , Rn} 和 {C1,C2, · · · ,Cm} ,因此,对于第 r 行第 c 列的格子 (r, c) ,其权重为一个二 元组 (Rr ,Cc) 。 小蓝可以在格子之间进行移动,若某时刻小蓝在格子 (r, c) ,那么他可以一 步走到任意的格子 (r ′ , c) 或 (r, c ′ ) ,其中 r ′ , c ′ 满足:
(1)Rr ′ > Rr ,Cc ′ > Cc ,
(2)@r ′′ .Rr ′ > Rr ′′ > Rr ; @c ′′ .Cc ′ > Cc ′′ > Cr 。 之后,老师提出了 T 个问题,第 i 个问题为:假设小蓝从格子 (s i r , s i c ) 出 发,移动到格子 (t i r , t i c ) 有多少种不同的走法,答案对 1000000007 取模。
【输入格式】 输入的第一行包含三个正整数 n, m, T ,相邻整数之间使用一个空格分隔。 第二行包含 n 个正整数 R1, R2, · · · , Rn ,相邻整数之间使用一个空格分隔。 第三行包含 m 个正整数 C1,C2, · · · ,Cm ,相邻整数之间使用一个空格分隔。 接下来 T 行,第 i 行包含四个正整数 s i r , s i c , t i r , t i c ,相邻整数之间使用一个 空格分隔。
【输出格式】 输出 T 行,每行包含一个整数,依次表示每个问题的答案。
【样例输入】
4 4 2
4 2 3 1
2 1 2 1
4 4 1 1
2 2 2 4
【样例输出】
4
0
【样例说明】
询问 1:
(4, 4) → (2, 4) → (3, 4) → (1, 4) → (1, 1);
(4, 4) → (2, 4) → (3, 4) → (3, 1) → (1, 1);
(4, 4) → (2, 4) → (2, 1) → (3, 1) → (1, 1);
(4, 4) → (4, 1) → (2, 1) → (3, 1) → (1, 1)。
询问 2:
不存在方案可以从 (2, 2) 走到 (2, 4) 。
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n, m, T ≤ 10^3 ;
对于所有评测用例,1 ≤ n, m, T ≤ 10^5 ,1 ≤ Ri ,Ci ≤ 10^8 ,1 ≤ s i r , t i r ≤ n , 1 ≤ s i c , t i c ≤ m 。
没时间思考了,我被智力测试了,很明显我被刷了。
不过我觉得这道题目应该是dp。
试题 H: 最大异或结点
时间限制: 10.0s 内存限制: 512.0MB 本题总分:20 分
【问题描述】
小蓝有一棵树,树中包含 N 个结点,编号为 0, 1, 2, · · · , N − 1 ,其中每个 结点上都有一个整数 Xi 。他可以从树中任意选择两个不直接相连的结点 a 、b 并获得分数 Xa ⊕ Xb ,其中 ⊕ 表示按位异或操作。 请问小蓝可以获得的最大分数是多少?
【输入格式】 输入的第一行包含一个整数 N ,表示有 N 个结点。 第二行包含 N 个整数 X1, X2, · · · , XN ,相邻整数之间使用一个空格分隔。 第三行包含 N 个整数 F1, F2, · · · , FN ,相邻整数之间使用一个空格分隔, 其中第 i 个整数表示 i 的父结点编号,Fi = −1 表示结点 i 没有父结点。
【输出格式】 输出一行包含一个整数表示答案。
【样例输入】
5
1 0 5 3 4
-1 0 1 0 1
【样例输出】
7
【样例说明】
选择编号为 3 和 4 的结点,x3 = 3 ,x4 = 4 ,他们的值异或后的结果为 3 ⊕ 4 = 7 。
【评测用例规模与约定】
对于 50% 的评测用例,1 ≤ N ≤ 1000 ;
对于所有评测用例,1 ≤ N ≤ 10^5 , 0 ≤ Xi ≤ 2^31 − 1 ,−1 ≤ Fi ≤ N ,Fi , 0 。
思路:这种题目,我不暴搜都对不起N<1000的50%的情况,主要是我也想不出好的方法。
代码:
n = int(input())
node = list(map(int, input().split(" ")))
pivot = list(map(int, input().split(" ")))
res = 0 # 防止只有一个节点
for i in range(n):
for j in range(n):
if i != j: # 选择2个节点
if pivot[j] != i and pivot[i] != j: # 不相连
res = max(res, node[i] ^ node[j])
print(res)
起码有50%的分好吧。
总结
感觉总体来讲,今年的我写出来的题目偏dfs,剪枝,图,可惜我剪枝不是很熟练,时间复杂度偏高,只会暴搜,不过有些题目也能写出来。
对一些常见的算法也考察了,比如吊坠,感觉出的很好,就是我写的挺拉跨的。
后记
20240507更新
拿了陕西省蓝桥杯省一,后面去算法网站核对了一下所有代码的准确性
填空第一题想错了,准确答案为:5435122
写了的题目都没有拿满分的,不过步骤分都拿到了。算法时间复杂度都很高,不少都超时了哎...
不过结果还行
备战国赛了!!