预计3月5日 Wednesday 前完成
【2025年3月1日,记】题目太简单了,3月3日前完成
蓝桥杯速成刷题清单(上)
(那1是蓝桥公园,Floyd模板题,卡python呢这)
目录
进度
【2025年3月5日22点46分,记】我才写完,水流那道二分还没有彻底搞懂,本来是说要在3月2日完成的,逾期太严重了,下不为例
题解和碎碎念
1. 排序
题面
太容易,快排内置的 sorted()
小结
ac代码
import os
import sys
# 请在此输入您的代码
N = int(input())
line = sys.stdin.readline().strip()
ls = list(map(int, line.split()))
sorted1 = sorted(ls)
sorted2 = sorted(ls, key= lambda x:-x)
print(" ".join(map(str, sorted1)))
print(" ".join(map(str, sorted2)))
2. 走迷宫
题面
太 ez,就一个 DFS,用内置的 queue
小结
python 内置 queue 用法简介
import queue
q = queue.Queue()
q.put((x, y, cnt)) # 放入
(x, y, cnt) = q.get() # 取出
if q.empty(): # 判断是否为空
pass
python 内置集合 set() 用法
visited = set()
visited.add((x, y))
if (x, y) in visited:
pass
visited.discard((x, y)) # 从集合中删除元素,若不存在,不报错
removed = visited.pop() # 随机删一个,并返回,为空时报错
visited.clear() # 清空整个集合
if len(visited) == 0:
print("Visited is empty")
ac代码
import os
import sys
import queue
# 请在此输入您的代码
line = sys.stdin.readline().strip()
N, M = map(int, line.split())
matrix = []
for _ in range(N):
line = sys.stdin.readline().strip()
ls = list(map(int, line.split()))
matrix.append(ls)
line = sys.stdin.readline().strip()
xin, yin, xout, yout = map(int, line.split())
xin -= 1
yin -= 1
xout -= 1
yout -= 1
moves = [[-1, 0], [0, -1], [1, 0], [0, 1]]
q = queue.Queue()
q.put((xin, yin, 0))
visited = set()
success = False
while not q.empty() and not success:
# print(queue.popleft())
(x, y, cnt) = q.get()
# print(f'x= {x}, y= {y}, cnt= {cnt}')
for move in moves:
x1 = x + move[0]
y1 = y + move[1]
if x1 < 0 or x1 >= N or y1 < 0 or y1 >= M or matrix[x1][y1] != 1 or (x1, y1) in visited:
pass
else:
if x1 == xout and y1 == yout:
print(cnt + 1)
success = True
break
else:
q.put((x1, y1, cnt + 1))
visited.add((x1, y1))
if not success:
print("-1")
3. 小明的背包1
题面
太 ez,最最最基础的 01 背包(当然,我不会啦~
小结
这篇讲的好好(虽然我只学了最基础的 01 背包,难的以后我遇到再学,一定会学的
背包问题详解(01背包,完全背包,多重背包,分组背包)
用二维 dp 记录,核心代码如下
# dp[m][n] 考虑m件物品,在空间不超过n时最大收益
dp = [[0 for _ in range(V + 1)] for _ in range(N + 1)]
for j in range(1, V + 1):
for i in range(1, N + 1):
dp[i][j] = dp[i - 1][j]
if item[i - 1][0] <= j:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - item[i - 1][0]] + item[i - 1][1])
有两个易错点:
- 那个 dp 初始化,我最开始不是用的两个 range,后一个是直接 * 。结果就遇到了 python 的 生成多个对同一行列表的引用,而不是独立的列表,这会导致修改其中任何一行时,其他行也会受到影响 debug 好一会…
- 忘记了 if 前面那个初始化,wa 了一半测试点…
ac代码
import sys
line = sys.stdin.readline().strip()
N, V = map(int, line.split())
item = []
for _ in range(N):
line = sys.stdin.readline().strip()
w, v = map(int, line.split())
item.append([w, v])
# dp[m][n] 考虑m件物品,在空间不超过n时最大收益
dp = [[0 for _ in range(V + 1)] for _ in range(N + 1)]
for j in range(1, V + 1):
for i in range(1, N + 1):
dp[i][j] = dp[i - 1][j]
if item[i - 1][0] <= j:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - item[i - 1][0]] + item[i - 1][1])
print(dp[N][V])
4. 蓝桥公园
题面
ez,Floyd 模板题,骂骂咧咧,什么破题,故意卡 python 是吧
小结
多源最短通路 Floyd 算法——3个循环,通过中心点更新
# i:中间点,j k:起点 终点
# idx2content:记录路径
for i in range(1, N + 1):
for j in range(1, N + 1):
for k in range(1, N + 1):
if dist[j][k] > dist[j][i] + dist[i][k]:
dist[j][k] = dist[j][i] + dist[i][k]
dist[k][j] = dist[j][k]
idx2content[j][k] = i
idx2content[k][j] = i
补充 单源最短路径 Dijkstra算法
讲的很好
补充 最小生成树
# 这个代码不对哈
# 最小不是从 dist_new,应该是 visited 里有的点到外界的最小
idx2content = [float('inf') for _ in range(N + 1)]
while len(visited) != N:
dist_new = copy.deepcopy(dist[new])
while True:
min_value = min(dist_new)
min_idx = dist_new.index(min_value)
if min_idx not in visited:
idx2content[new] = min_idx
new = min_idx
visited.add(min_idx)
dist_new[min_idx] = float('inf')
ps. 笑死了,最开始把最小生成树当成 Floyd 交了,还过了一些数据点
ac代码
略
5. 回文判定
题面
秒了
小结
ac代码
import sys
line = sys.stdin.readline().strip()
if line == line[::-1]:
print("Y")
else:
print("N")
6. 小明的彩灯
题面
ez,基础的前缀和的题目
小结
ac代码
import sys
line = sys.stdin.readline().strip()
N, Q = map(int, line.split())
line = sys.stdin.readline().strip()
lights = list(map(int, line.split()))
lights.insert(0, 0)
mark = [0 for _ in range(N + 3)]
for _ in range(Q):
line = sys.stdin.readline().strip()
l, r, c = map(int, line.split())
mark[l] += c
mark[r + 1] -= c
now = 0
for i in range(N + 1):
now += mark[i]
lights[i] += now
if lights[i] < 0:
lights[i] = 0
print(" ".join(map(str, lights[1:])))
7. 解立方根
题面
如题目,就是解立方根,保留3位小数
小结
写的时候,就在想,不会这么简单吧,直接 **1/3
这不合理吧老师,但是这题单又这么 ez
肯定要卡精度,不知道 **1.3
精度够不够,ok果然不够
发现标签给的是二分,what?二分?
居然真的可以用二分
ac代码
8. 蓝桥幼儿园
题面
并查集
小结
没做过并查集(惭愧啊惭愧啊,快学三年编程了)
知道是集合的 union,然后TLE了
看看用 set()
干了些啥
if y0 == -1 and x0 == -1:
setName = 'set' + str(setNum)
globals()[setName] = set() # 创建新的 set
globals()[setName].add(y)
globals()[setName].add(x)
setNum += 1
elif x0 == -1:
setName = 'set' + str(y0) # 放入即可
globals()[setName].add(x)
elif y0 == -1:
setName = 'set' + str(x0) # 放入即可
globals()[setName].add(y)
else:
setX0 = 'set' + str(x0)
setY0 = 'set' + str(y0)
# 两个 set 合并
globals()[setX0] = globals()[setX0].union(globals()[setY0])
# 在其中一个作标志弃用
globals()[setY0].add(-1)
超时原因分析
- 数据结构的问题
使用了多个集合(set1, set2, etc.)来管理朋友关系,并且每次都通过遍历所有集合来查找或合并集合。这种方法的时间复杂度非常高。原来每次判断是不是in set1
这些都是遍历… - 动态变量名的问题
使用了 globals() 来动态创建和访问变量名,这种方法在性能上并不高效,尤其是在频繁操作时。 - 缺乏路径压缩和按秩合并
在并查集问题中,路径压缩和按秩合并是优化查找和合并操作的关键。你的代码中没有使用这些优化,导致查找和合并操作的时间复杂度较高。
并查集
两个关键函数,找根 root()
函数和合并 union()
函数
root() 函数
def root(x):
if x != F[x]:
F[x] = root(F[x])
return F[x]
递归实现,每次还在偷偷压缩路径 F[x] = root(F[x])
啧啧啧,心机(其实是太巧妙啦
union() 函数
def union(x, y):
xr = root(x)
yr = root(y)
if xr != yr:
F[xr] = yr
可以这样写吗👇
def union(x, y):
F[x] = y
不能!!! 举例 F[]
如下
1 2 3 4 5
合并12 45 -> 1 1 3 4 4
合并35 -> 1 1 3 4 3
请问45什么关系 ???
ac代码
import sys
line = sys.stdin.readline().strip()
N, M = map(int, line.split())
F = [i for i in range(N + 1)]
def root(x):
if x != F[x]:
F[x] = root(F[x])
return F[x]
def union(x, y):
xr = root(x)
yr = root(y)
if xr != yr:
F[xr] = yr
setNum = 1
ans = []
for _ in range(M):
line = sys.stdin.readline().strip()
op, x, y = map(int, line.split())
if op == 1: # be friend
union(x, y)
else:
xr = root(x)
yr = root(y)
if xr == yr:
ans.append('YES')
else:
ans.append('NO')
for row in ans:
print(row)
9. 蓝桥王国
题面
dijkstra 模板题,要用优先队列 O(nlogn)
,反之超时
小结
看上面的图,原理就是这个原理 每次选距离s最近的点,然后更新
朴素的实现不罗嗦——用二维矩阵存,然后复杂度是 O(n^2)
用优先队列 PriorityQueue
,用法👇
from queue import PriorityQueue
# init
q = PriorityQueue()
q.put((a, b)) # 会以a从小到大排序,注意是(())!!! not ([])
while q.queue: # 当q不为空
dis, u = q.get() # 取
dijkstra 核心,维护 priorityQueue,visited、distance,板子👇
# 以s为起点的 单源最短路径
def dijkstra(s):
visited = [False for _ in range(N + 1)]
q = PriorityQueue()
distance = [float('inf') for _ in range(N + 1)]
distance[s] = 0
# 真的觉得下面这行太巧妙了,把初始化的问题完美解决
q.put((0, s))
while not q.empty():
dis, u = q.get()
if visited[u]:
continue
visited[u] = True
# 核心的话一个是这个for循环
for v, w in dist[u].items():
if distance[v] > distance[u] + w:
distance[v] = distance[u] + w
q.put((distance[v], v))
ans = []
for i in range(1, N + 1):
if not visited[i]:
distance[i] = -1
ans.append(distance[i])
print(' '.join(map(str, ans)))
ac代码
import sys
from queue import PriorityQueue
line = sys.stdin.readline().strip()
N, M = map(int, line.split())
dist = [[] for _ in range(N + 1)]
for _ in range(M):
line = sys.stdin.readline().strip()
u, v, w = map(int, line.split())
dist[u].append((w, v))
def dijkstra(s):
# 以s为起点的单源最短
visited = [False for _ in range(N + 1)]
q = PriorityQueue()
distance = [float('inf') for _ in range(N + 1)]
distance[1] = 0
q.put((0, s))
while q.queue:
dis, u = q.get()
if visited[u]:
continue
visited[u] = True
for w, v in dist[u]:
if distance[v] > distance[u] + w:
distance[v] = distance[u] + w
q.put((distance[v], v))
ans = []
for i in range(1, N + 1):
if not visited[i]:
distance[i] = -1
ans.append(distance[i])
print(' '.join(map(str, ans)))
dijkstra(1)
10. 蓝桥骑士
题面
求 最长单调递增子序列 长度
小结
emmm,入门题目吧,感觉是 dp
果然也是 dp,只是自己求不出来
dp[x]
从 0 - x 最长子序列长度
错误想法如下:
0 - (x - 1) 最长是 dp[x - 1],如果 arr[x] > (arr[0 … (x - 1)] 里最大的),那么最大长度 +1
请看反例: 98 99 0 1 2
这个的复杂度是 O(n)
有一点子对的,错误的是每次只看了前一个 dp,应该是这样子:
“以nums[i] 为结尾的最长递增子序列的长度可以由 nums[0] 为结尾的最长递增子序列长度、nums[1] 为结尾的最长长度、……nums[n - 1] 为结尾的最长长度 比较得到”
两层 for
循环
import sys
N = int(input())
line = sys.stdin.readline().strip()
arr = list(map(int, line.split()))
dp = [1 for _ in range(N)]
for i in range(1, N):
for j in range(0, i):
if arr[i] > arr[j]:
dp[i] = max(dp[i], dp[j] + 1)
print(sorted(dp, key= lambda x:-x)[0])
嘻嘻,你猜我为什么不把代码贴在 ac(超时了呗笨蛋
正确方法:
啊啊啊啊 w(゚Д゚)w 真的的是甜菜,我们都可以感受到,如果一个LIS(LIS Longest Increasing Subsequence,单调递增子序列)结尾元素越小,那它是不是最有可能是最长的
一位同学的思路(侵删),真的好聪明,复杂度变成 O(nlogn)
狗尾续貂一下:最后的B数组一定单调增,但它的顺序可能不是原序列出现的顺序,比如说,原序列 1 2 99 100 3
那它就是 1 2 3 100
3
插队了吗,插队了,但是,它展现出来的是 1 2 99 100
的子序列,如果 3
后面是 5 6 7
那么必然更新掉了 100
(看出玄机了吧
在批判中继承
bisect.bisect_left 函数
二分查找函数,返回的是一个插入点,使得插入该点后,列表仍然保持有序
import bisect
target = bisect,bisect_left(a, x, lo=0, hi=len(a))
# a:序列,x查找目标值
# 可选,查找范围,默认整个list
# target:插入x的位置
ac代码
import sys
import bisect
N = int(input())
line = sys.stdin.readline().strip()
arr = list(map(int, line.split()))
B = [arr[0]]
for i in range(1, N):
target = bisect.bisect_left(B, arr[i])
if target == len(B):
B.append(arr[i])
else:
B[target] = arr[i]
print(len(B))
总结
“且将新火试新茶,诗酒乘年华”
这次的题单逾期了,下不为例,加油!