蓝桥杯做题笔记

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\, i 的阀门会在 S\, i 时刻打开,并不断让水流入管道。

水会从两个最外侧满足条件的阀门流入,然后逐渐向内流至下一个没开的阀门

二分答案,将每个阀门扩为区间,进行区间合并,检查是否能覆盖整个管道

二分: 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列表,复杂度 O(n^{2}) \rightarrow O(n)。注释掉的是暴力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]    往后的合法终点个数  

终点前缀和数组恰好可以这项任务,遍历两次数组,时间复杂度O(2n)

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

  • 15
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值