lqb官方题单-速成刷题清单(上) - python版

预计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])

有两个易错点:

  1. 那个 dp 初始化,我最开始不是用的两个 range,后一个是直接 * 。结果就遇到了 python 的 生成多个对同一行列表的引用,而不是独立的列表,这会导致修改其中任何一行时,其他行也会受到影响 debug 好一会…
  2. 忘记了 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)

超时原因分析

  1. 数据结构的问题
    使用了多个集合(set1, set2, etc.)来管理朋友关系,并且每次都通过遍历所有集合来查找或合并集合。这种方法的时间复杂度非常高。原来每次判断是不是 in set1 这些都是遍历…
  2. 动态变量名的问题
    使用了 globals() 来动态创建和访问变量名,这种方法在性能上并不高效,尤其是在频繁操作时。
  3. 缺乏路径压缩和按秩合并
    在并查集问题中,路径压缩和按秩合并是优化查找和合并操作的关键。你的代码中没有使用这些优化,导致查找和合并操作的时间复杂度较高。

并查集
两个关键函数,找根 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))

总结

“且将新火试新茶,诗酒乘年华”
这次的题单逾期了,下不为例,加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值