2023 python B
1.2023 - 蓝桥云课 (lanqiao.cn)
主要是理解题意和对py熟练度有要求,代码挺简洁的
li = [i for i in range(12345678, 98765433)]
ans = 0
def sol(x):
x = str(x)
p1 = x.find('2')
p2 = x.find('0', p1 + 1)
p3 = x.find('2', p2 + 1)
p4 = x.find('4', p3 + 1)
if p1 < p2 < p3 < p4:
return False
else:
return True
for i in li:
if sol(i):
ans += 1
print(ans)
2.硬币兑换 - 蓝桥云课 (lanqiao.cn)
暴力枚举,当进行完所有循环,答案列表中存储的是 把这个面值作为目标来转化,最后能得到的数目。
对于答案列表中某个值 ans[i] = x,i 是所有小于i的值进行两两相加的结果,贪心的想这样枚举似乎是对的,严格的证明,buhui...
li = [0] * 5000
x = int(input())
for i in range(1,x + 1):
li[2 * i] += i // 2
for j in range(i + 1, x + 1):
li[i + j] += i
z = max(li)
print(z)
3.松散子序列 - 蓝桥云课 (lanqiao.cn)
状态划分为两个,一个是上一个选了这个不能选,一个是上一个没选这个可以选(当然也可以不选
朴素dp
s = input()
dp = [[0, 0] for i in range(10 ** 6 + 5)]
dp[1][1] = ord(s[0]) - ord('a') + 1
for ii in range(1, len(s)):
i = ii + 1
dp[i][1] = max(dp[i - 2][1], dp[i - 1][0], dp[i - 2][0]) + ord(s[ii]) - ord('a') + 1
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1])
print(max(dp[len(s)][0], dp[len(s)][1]))
优化
1.字符串先算好存起来
2.dp数组不需要开两维,到某个字母时的max值是绝对的,所以不需要额外开一维标记是否拿取,字母的位置蕴含着这个信息
s = input()
li = [0]
dp = [0] * (10**6+5)
for i in range(len(s)):
li.append(ord(s[i])-ord('a')+1)
dp[1] = li[1]
for i in range(2,len(s)+1):
dp[i] = max(dp[i-1],dp[i-2]+li[i])
print(dp[len(s)])
4.管道 - 蓝桥云课 (lanqiao.cn)
一开始管道是空的,位于 的阀门会在 时刻打开,并不断让水流入管道。
水会从两个最外侧满足条件的阀门流入,然后逐渐向内流至下一个没开的阀门
二分答案,将每个阀门扩为区间,进行区间合并,检查是否能覆盖整个管道
二分: l = mid 找满足条件的最后一个数 (小于tar的最靠近tar的数)
r = mid 找满足条件的第一个数 (大于tar的第一个数)
满足条件是说可以覆盖整个管道,第一个数符合最早时间的要求,应用 r = mid
n, m = map(int, input().split())
pp = []
for i in range(n):
pp.append(list(map(int, input().split())))
def check(x):
q = []
for i in pp:
q.append([i[0] - x + i[1], i[0] + x - i[1]])
q = sorted(q, key=lambda x: x[0])
if q[0][1] < 1:
return False
tmp = q[0][1]
for i in q:
if tmp >= i[0] - 1:
tmp = max(tmp, i[1])
return tmp >= m
l, r = 0, 10 ** 9 + 5
while l < r:
mid = (l + r) // 2
if check(mid):
r = mid
else:
l = mid + 1
print(l)
5.保险箱 - 蓝桥云课 (lanqiao.cn)
Hint 1
每位进位退位只会影响高一位
Hint2
状态转移:前一位退位,前一位进位,前一位不进退
n = int(input())
x = [0] + list(map(int, input()))[::-1]
y = [0] + list(map(int, input()))[::-1]
dp = [[0, 0, 0] for i in range(n + 4)]
dp[1][0] = x[1] + 10 - y[1] # 退位
dp[1][1] = abs(y[1] - x[1])
dp[1][2] = y[1] + 10 - x[1] # 进位
for i in range(2, n + 1):
dp[i][0] = min(dp[i - 1][0] + x[i] - 1 + 10 - y[i], dp[i - 1][1] + x[i] + 10 - y[i],
dp[i - 1][2] + 11 + x[i] - y[i])
dp[i][1] = min(dp[i - 1][0] + abs(y[i] - x[i] + 1), dp[i - 1][1] + abs(x[i] - y[i]),
dp[i - 1][2] + abs(y[i] - x[i] - 1))
dp[i][2] = min(dp[i - 1][0] + y[i] + 10 - x[i] + 1, dp[i - 1][1] + y[i] + 10 - x[i],
dp[i - 1][2] + y[i] + 10 - x[i] - 1)
print(min(dp[n]))
每次加减的公式在 5、6、7行套进去,x[i]根据进位和退位的要求加减1
6.树上选点 - 蓝桥云课 (lanqiao.cn)
鼻子气歪了,刚开始没读懂题,以为是松散子序列,完了发现是没有上司的舞会 的变式...
没有上司的舞会,不是直接上司就可以选
在本题中
1.每层只能选一个
2.父亲选了不能选其孩子
不会做,有空补上
感谢AcWing 蓝桥杯. [蓝桥杯2023初赛] 树上选点 - AcWing 此篇题解
tr 存树,ceng存每一层的节点, zt[i]存第i层可以转移的状态,存的是具体的权值累计
从最底层向上遍历,取出每层(i)的每个节点(j),先进行不要这个节点的操作,若不要这个节点,则dp[j][0]值为比该层更深一层zt中的最大值
在就是要这个节点,最小即为该点权值,先赋上最小值
然后把比该层最深一层的st中属于该点儿子的值删掉,进行转移判断,若比该层最深一层的st非空,说明有可以转移的点,取一个最大值进行转移
因为要继续判断该层的其他点,所以再把该点的儿子全加入到比该层最深一层的st中
然后把我们得到的这个点的值加入到属于该层的st中,以便i-1即遍历完该层走到比该层更浅一层时判断
from collections import defaultdict
n = int(input())
N = n+5
tr = defaultdict(list)
ceng = defaultdict(list)
zt = defaultdict(list)
li = [0, -1] + list(map(int, input().split()))
w = [0] + list(map(int, input().split()))
d = [0] * N
d[1] = 1
for i in range(2, n + 1):
tr[li[i]].append(i)
d[i] = d[li[i]] + 1
for i in range(1,n+1):
ceng[d[i]].append(i)
dp = [[0, 0] for i in range(N)]
for i in range(max(d),0,-1):
for j in ceng[i]:
if zt[i+1]:
dp[j][0] = max(dp[j][0],max(zt[i+1]))
dp[j][1] = w[j]
for k in tr[j]:
zt[i+1].pop(zt[i+1].index(dp[k][1]))
if zt[i+1]:
dp[j][1]=max(dp[j][1],w[j]+max(zt[i+1]))
for k in tr[j]:
zt[i+1].append(dp[k][1])
zt[i].append(dp[j][0])
zt[i].append(dp[j][1])
print(max(dp[1]))
4.飞机降落 - 蓝桥云课 (lanqiao.cn)
vis标记是否降落
搜索传递的参数为 dfs(当前时间,递归深度)
若出现 当前时间 大于 某个未起飞飞机的 最晚起飞时间 的情况,剪枝
递归深度达到n,全部可以降落,全局变量f设置为1
全局变量f global f 每个函数都要定义
def sol():
global f
n = int(input())
li = []
vis = [0] * (n + 5)
for i in range(n):
li.append(list(map(int, input().split())))
f = 0
def dfs(t, p):
global f
if p == n:
f = 1
return
for i in range(n):
if vis[i] == 1:
continue
if t > li[i][0] + li[i][1]:
return
if t <= li[i][0] + li[i][1]:
vis[i] = 1
dfs(max(t, li[i][0]) + li[i][2], p + 1)
vis[i] = 0
dfs(0, 0)
if f == 1:
print("YES")
else:
print("NO")
N = int(input())
for _ in range(N):
sol()
5.接龙数列 - 蓝桥云课 (lanqiao.cn)
求删去的最少个数,可以转化为求剩下的最长个数,类似于最长上升子序列的dp题,第i次的状态可以通过前i次的状态转化而来。
值得注意的是,对于接龙这个操作,我们更需要关注的是每个字符串的首尾字母,不同于 上升子序列 题目中 “序列” 这个强大的约束,根据这个要点进行优化,只维护以某字母结尾的最长序列的长度,即长度为10的dp列表,复杂度 。注释掉的是暴力dp
# n = int(input())
# li = list(input().split())
# dp = [1] * (n+5)
# for i in range(1,n):
# for j in range(i):
# if li[i][0] == li[j][-1]:
# dp[i] = max(dp[i],dp[j]+1)
# print(n-max(dp))
from collections import defaultdict
n = int(input())
li = list(input().split())
dp = defaultdict(int)
m = 0
for i in range(n):
tmp = li[i][-1]
dp[tmp] = max(dp[tmp],dp[li[i][0]]+1)
print(n-max(dp.values()))
6.岛屿个数 - 蓝桥云课 (lanqiao.cn)
搜索还要加强练习,太笨了
找到一个岛屿很简单,现在需要解决的问题是,如果小岛套在大岛里,那么就不算是岛屿
换个角度看,若陆地周围有最外圈的海水,则说明这个陆地位于最外圈的大岛
BFS最外圈海水,标记为-1,注意斜的陆地挡不住水,所以注意是八连通
BFS岛屿,判断八连通格子中是否有-1,若是进行四联通的岛屿染色,res记录岛屿个数
T = int(input())
dx, dy = [1, -1, 0, 0, 1, -1, 1, -1], [0, 0, 1, -1, 1, -1, -1, 1]
while T:
m, n = map(int, input().split())
li = [[0] * (n + 2)]
for i in range(m):
tmp = [0] + list(map(int, input())) + [0]
li.append(tmp)
li.append([0] * (n + 2))
vis = [[0] * (n + 2) for i in range(m + 2)]
res = 0
q = []
q.append([0, 0])
while q:
tmp = q.pop(0)
x, y = tmp[0], tmp[1]
for i in range(8):
nx, ny = x + dx[i], y + dy[i]
if 0 <= nx < m + 2 and 0 <= ny < n + 2 and vis[nx][ny] == 0 and li[nx][ny] == 0:
vis[nx][ny] = 1
li[nx][ny] = -1
q.append([nx, ny])
for i in range(1, m + 1):
for j in range(1, n + 1):
if li[i][j] > 0 and vis[i][j] == 0:
f = 0
for k in range(8):
nx, ny = i + dx[k], j + dy[k]
if li[nx][ny] == -1:
f = 1
if f == 0:
continue
sx, sy = i, j
q = []
q.append([sx, sy])
while q:
tmp = q.pop(0)
x, y = tmp[0], tmp[1]
for k in range(4):
nx, ny = x + dx[k], y + dy[k]
if 1 <= nx <= m and 1 <= ny <= n and vis[nx][ny] == 0 and li[nx][ny] == 1:
vis[nx][ny] = 1
q.append([nx, ny])
res += 1
print(res)
T -= 1
7.子串简写 - 蓝桥云课 (lanqiao.cn)
看了一圈题解发现没有我这个做法,记录一下
先处理一下终点的前缀和
只关注起始位置的字符,遍历字符串,若遇到起点,就进行一次累加
累加的值为从 索引[起点+k-2] 往后的合法终点个数
终点前缀和数组恰好可以这项任务,遍历两次数组,时间复杂度
k = int(input())
q, a, b = input().split()
q = list(q)
L = len(q)
nb = [0] * (5 * 10 ** 5 + 5)
for i in range(L):
nb[i] = nb[i - 1]
if q[i] == b:
nb[i] += 1
ans = 0
for i in range(L):
if q[i] == a and i + k - 2 < L:
ans += nb[L-1] - nb[i + k - 2]
print(ans)
9.异或和 - 蓝桥云课 (lanqiao.cn)
嘻嘻,看了一圈没有一样的,快快让我端上来罢!
重点在于题目告诉了哪两个点有边,却没有说父子关系
所以先双向建树,然后从根1开始,删掉回路
异或和可暴力求解
from collections import defaultdict
tr = defaultdict(list)
n, m = map(int, input().split())
def dfs(x):
res = w[x]
for i in tr[x]:
res ^= dfs(i)
return res
def bui(x):
for i in tr[x]:
if x in tr[i]:
tr[i].pop(tr[i].index(x))
bui(i)
w = [0] + list(map(int, input().split()))
for i in range(n - 1):
a, b = map(int, input().split())
tr[a].append(b)
tr[b].append(a)
bui(1)
for i in range(m):
li = list(map(int, input().split()))
if li[0] == 1:
w[li[1]] = li[2]
else:
print((dfs(li[1])))