当前位置:首页 » 《随便一记》 » 正文

2023年第十四届蓝桥杯省赛Python大学B组真题解析

1 人参与  2024年02月28日 10:31  分类 : 《随便一记》  评论

点击全文阅读


写在前面

⚠️写这份题解之前我是没有看过任何版本的题解,以下代码均是我独立AC后把代码记录到该题解内。

?代码提交后是能保证100%通关的,并且配有注释,可以放心食用。

C题 松散子序列???(10分)

题目描述

给定一个仅含小写字母的字符串 s ,假设 s 的一个子序列 t 的第 i 个字符 对应了原字符串中的第 pi 个字符。我们定义 s 的一个松散子序列为:对于 i > 1 总是有 pi − pi−1 ≥ 2 。设一个子序列的价值为其包含的每个字符的价值之和 ( a ∼ z 分别为 1 ∼ 26 ) 。

求 s 的松散子序列中的最大价值。

输入格式

输入一行包含一个字符串 s 。

输出格式

输出一行包含一个整数表示答案。

样例输入

azaazaz

样例输出

78

代码解析

s = [0] + list(input())for i in range(1, len(s)): s[i] = ord(s[i]) - 96dp = [0] * (len(s) + 1) # dp[i]表示前i个数的最大价值dp[1] = s[1]for i in range(2, len(s)):  # 第i个数不选的话从dp[i-1]直接递推过来,不选的话从dp[i-2]递推过来,满足选的数都至少间隔1个。    dp[i] = max(dp[i - 1], dp[i - 2] + s[i])print(dp[len(s) - 1])

D题 管道???(10分)

题目描述

有一根长度为 len 的横向的管道,该管道按照单位长度分为 len 段,每一段的中央有一个可开关的阀门和一个检测水流的传感器。

一开始管道是空的,位于 Li 的阀门会在 Si 时刻打开,并不断让水流入管道。

对于位于 Li 的阀门,它流入的水在 Ti (Ti ≥ Si) 时刻会使得从第 Li−(Ti−Si) 段到第 Li + (Ti − Si) 段的传感器检测到水流。 求管道中每一段中间的传感器都检测到有水流的最早时间。

输入格式

输入的第一行包含两个整数 n, len,用一个空格分隔,分别表示会打开的阀门数和管道长度。

接下来 n 行每行包含两个整数 Li , Si,用一个空格分隔,表示位于第 Li 段 管道中央的阀门会在 Si 时刻打开。

输出格式

输出一行包含一个整数表示答案。

样例输入

3 101 16 510 2

样例输出

5

代码解析

这道题满足答案的二段性,也是最大值最小化问题,可以用二分法求解。

n, ll = map(int, input().split())a = []for _ in range(n):    a.append(list(map(int, input().split())))def check(ans):    res = [] # res存储管道的灌溉区间    for i in range(n):        if ans < a[i][1]: continue        l = max(1, a[i][0] - (ans - a[i][1]))        r = min(ll, a[i][0] + (ans - a[i][1]))        res.append([l, r])    res.sort()    # 如果最左起点不能灌溉到1管道则False    if res[0][0] > 1: return False    rr = res[0][1] # 初始化最右灌溉区域    # 合并区间    for i in range(1, len(res)):      # 如果该区间和上一区间没有交集且不连续则False        if res[i][0] > rr and res[i][0] != rr + 1: return False        else: rr = max(res[i][1], rr) # 更新最右灌溉区域    if rr < ll: return False # 如果最右灌溉区域达不到Len则False    return True# r必须开到2e9l, r = 1, 2 * 10**9while l + 1 != r:    mid = (l + r) // 2    if check(mid):        r = mid    else: l = midprint(r)

E题 保险箱???✨(15分)

题目描述

小蓝有一个保险箱,保险箱上共有 n 位数字。

小蓝可以任意调整保险箱上的每个数字,每一次操作可以将其中一位增加 1 或减少 1 。

当某位原本为 9 或 0 时可能会向前(左边)进位/退位,当最高位(左边第 一位)上的数字变化时向前的进位或退位忽略。

例如:

00000 的第 5 位减 1 变为 99999 ;

99999 的第 5 位减 1 变为 99998 ;

00000 的第 4 位减 1 变为 99990 ;

97993 的第 4 位加 1 变为 98003 ;

99909 的第 3 位加 1 变为 00009 。

保险箱上一开始有一个数字 x,小蓝希望把它变成 y,这样才能打开它,问 小蓝最少需要操作的次数。

输入格式

输入的第一行包含一个整数 n 。

第二行包含一个 n 位整数 x 。

第三行包含一个 n 位整数 y 。

输出格式

输出一行包含一个整数表示答案。

样例输入

51234954321

样例输出

11

代码解析

从低位到高位依此变换,变换数字有三种情况:进位(高位+1)、退位(高位-1)、不进位也不退位。

n = int(input())x = [0] + list(map(int, list(input())))[::-1]y = [0] + list(map(int, list(input())))[::-1]# dp[i][j]表示i~n个数已经相等,j存第i个数相等的方式dp  = [[0] * 3 for i in range(n + 1)]dp[1][0] = abs(x[1] - y[1]) # 不进退位dp[1][1] = 10 - x[1] + y[1] # 进位dp[1][2] = 10 - y[1] + x[1] # 退位for i in range(2, n + 1):    dp[i][0] = min(dp[i - 1][0] + abs(x[i] - y[i]), dp[i - 1][1] + abs(x[i] - y[i] + 1), dp[i - 1][2] + abs(x[i] - y[i] - 1))    dp[i][1] = min(dp[i - 1][0] + 10 - x[i] + y[i], dp[i - 1][1] + 10 - (x[i] + 1) + y[i], dp[i - 1][2] + 10 - (x[i] - 1) + y[i])    dp[i][2] = min(dp[i - 1][0] + 10 - y[i] + x[i], dp[i - 1][1] + 10 - y[i] + (x[i] + 1), dp[i - 1][2] + 10 - y[i] + (x[i] - 1))print(min(dp[n][0], dp[n][1], dp[n][2]))

F题 树上选点????(15分)

题目描述

给定一棵树,树根为 1,每个点的点权为 Vi 。

你需要找出若干个点 Pi,使得:

每两个点 Px Py 互不相邻;

每两个点 Px Py 与树根的距离互不相同;

找出的点的点权之和尽可能大。

请输出找到的这些点的点权和的最大值。

输入格式

输入的第一行包含一个整数 n 。

第二行包含 n − 1 个整数 Fi ,相邻整数之间使用一个空格分隔,分别表示 第 2 至 n 个结点的父结点编号。

第三行包含 n 个整数 Vi,相邻整数之间使用一个空格分隔,分别表示每个结点的点权。

输出格式

输出一行包含一个整数表示答案。

样例输入

51 2 3 22 1 9 3 5

样例输出

11

代码解析

⚠️pypy3提交通过可以达到100%。

这题很难,需要用到树形dp,用state数组记录每一层的选了结点最大值a、不选结点的最大值b、选了结点和a非同父结点的最大值。参考代码理解,解法应该是全网最优解了。

n = int(input())fa = [0, 0] + list(map(int, input().split()))v = [0] + list(map(int, input().split())) # 存1~n个结点的值dep = [0] * (n + 1) # 每个结点的深度dep_node = [[] for i in range(n + 1)] # 存每个深度中的结点编号deep_max = 0 # 深度的最大值for i in range(1, n + 1): # 求每个深度有哪些结点    dep[i] = dep[fa[i]] + 1    deep_max = max(deep_max, dep[i])    dep_node[dep[i]].append(i)dp = [[0] * 2 for i in range(n + 1)] # 表示i结点选或不选的情况dp[i][0/1]state = [0, 0, 0] # [不选该层结点的最大值结点,选该层结点的最大值结点,选该层结点非最大值同父最大值结点]for i in range(deep_max, 0, -1): # 从deep_max~1深度,自下向上    for j in dep_node[i]: # j表示i深度的结点        dp[j][0] = max(dp[state[0]][0], dp[state[1]][1])        if fa[state[1]] == j: # 如果下一层的最大值结点是j的子结点, 则选择max(非j子结点的最大值, 不选的最大值结点)            dp[j][1] = v[j] + max(dp[state[0]][0], dp[state[2]][1])        else: # max(下一层的最大值结点, 不选的最大值结点)            dp[j][1] = v[j] + max(dp[state[0]][0], dp[state[1]][1])                state = [0, 0, 0]    for j in dep_node[i]: # 求深度为i的dp[j][0]和dp[j][1]的最大值        if dp[state[0]][0] < dp[j][0]:            state[0] = j        if dp[state[1]][1] < dp[j][1]:            state[1] = j                for j in dep_node[i]: # 求i深度下和选最大值结点非共同父结点的次最大值结点        if fa[state[1]] != fa[j] and dp[state[2]][1] < dp[j][1]:            state[2] = jprint(max(dp[1][0], dp[1][1]))

I题 异或和????(25分)

题目描述

给一棵含有 n 个结点的有根树,根结点为 1 ,编号为 i 的点有点权 ai(i ∈ [1, n])。现在有两种操作,格式如下:

• 1 x y 该操作表示将点 x 的点权改为 y 。

• 2 x 该操作表示查询以结点 x 为根的子树内的所有点的点权和。

现有长度为 m 的操作序列,请对于每个第二类操作给出正确的结果。

输入格式

输入的第一行包含两个正整数 n, m ,用一个空格分隔。

第二行包含 n 个整数 a1, a2, …, an ,相邻整数之间使用一个空格分隔。

接下来 n − 1 行,每行包含两个正整数 ui , vi ,表示结点 ui 和 vi 之间有一条边。

接下来 m 行,每行包含一个操作。

输出格式

输出若干行,每行对应一个查询操作的答案。

样例输入

4 51 2 3 41 21 32 42 11 1 02 12 2

样例输出

4566

代码解释

n, m = map(int, input().split())v = [0] + list(map(int, input().split()))g = [[] for _ in range(n + 1)]for _ in range(n - 1):    u, va = map(int, input().split())    g[u].append(va)    g[va].append(u)tree = [0] * (n + 1) # 用树状数组求异或和a = [[0, 0] for _ in range(n + 1)] # 存dfs序cnt = 0def dfs(node, fa): # 求dfs序    global cnt    cnt += 1    a[node][0] = cnt    for i in g[node]:        if i != fa:            dfs(i, node)    a[node][1] = cntdfs(1, 0)def lowbit(x):    return x & (-x)def update(x, d):    while x <= n:        tree[x] ^= d        x += lowbit(x)        def query(x):    ans = 0    while x:        ans ^= tree[x]        x -= lowbit(x)    return ansfor i in range(1, n + 1):  # 子结点的入序和出序一定是在根结点入序和出序之间    update(a[i][0], v[i])for _ in range(m):    o = list(map(int, input().split()))    if o[0] == 2:      # 从根结点的入序到出序之间的异或和        print(query(a[o[1]][1]) ^ query(a[o[1]][0] - 1))    else:        x, y = o[1], o[2]        update(a[x][0], v[x] ^ y)        v[x] = y

未完待续……继续补充中


点击全文阅读


本文链接:http://zhangshiyu.com/post/70803.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1