第十六届蓝桥杯Python大学B组试题详解:全方位解析A至H题解题思路与代码实现

第一次参加蓝桥杯软件赛 Python 大学 B 组,面对 8 道题从紧张到渐入佳境,每道题都像一场小型闯关。以下是从试题 A 到 H 的完整解析,包含题目解读、代码实现、答案和算法总结,希望能帮大家理清思路,掌握核心方法。

一、试题 A:攻击次数(结果填空)

题目

敌人初始血量 2025,三个英雄每回合攻击规则:

  1. 英雄 1:每回合固定 5 血
  2. 英雄 2:奇数回合 15 血,偶数回合 2 血
  3. 英雄 3:回合数 %3=1 时 2 血,%3=2 时 10 血,%3=0 时 7 血 求第几回合血量≤0。

解读

纯模拟题,按回合循环计算总伤害,累减血量直到结束。注意回合从 1 开始,条件判断别搞反(如奇数回合是turn%2==1)。

代码

hp = 2025
turn = 0
while hp > 0:
    turn += 1
    dmg1 = 5
    dmg2 = 15 if turn % 2 == 1 else 2
    mod = turn % 3
    dmg3 = 2 if mod == 1 else 10 if mod == 2 else 7
    hp -= dmg1 + dmg2 + dmg3
print(turn)  # 输出答案

答案

103

算法总结

  • 模拟思想:按步骤循环,逐个回合计算状态变化。
  • 条件分支:直接根据规则用if-else处理不同情况。
  • 二、试题 B:最长字符串(结果填空)

    题目

    定义 “优美字符串”:长度 1 或前 n-1 个字符调整顺序后存在一个更短的优美字符串。从单词本中找最长的,字典序最小的。

    解读

    1. 按长度排序单词,优先处理短字符串;
    2. 对每个单词,将前 n-1 个字符排序成集合,检查是否存在已记录的合法集合(动态规划)。

    代码

    with open('words.txt', 'r') as f:
        words = [line.strip() for line in f]
    
    # 按长度排序,同长度按字典序排序
    words.sort(key=lambda x: (len(x), x))
    max_len = 0
    best_word = ''
    # 记录每个长度的合法字符集合(排序后的字符串)
    valid_sets = {1: set()}  # 长度1的单词直接合法
    
    for word in words:
        n = len(word)
        if n == 1:
            valid_sets[1].add(''.join(sorted(word)))
            if n > max_len:
                max_len = n
                best_word = word
        else:
            # 前n-1个字符排序后的字符串
            prefix = ''.join(sorted(word[:-1]))
            # 检查是否存在长度n-1的合法集合
            if n-1 in valid_sets and prefix in valid_sets[n-1]:
                if n not in valid_sets:
                    valid_sets[n] = set()
                current_set = ''.join(sorted(word))
                valid_sets[n].add(current_set)
                if n > max_len or (n == max_len and word < best_word):
                    max_len = n
                    best_word = word
    
    print(best_word)  # 假设words.txt按题目示例输入,输出dbca
    

    答案

    假设输入示例中的单词,答案为 dbca(实际需根据题目给定的 words.txt 计算,此处以示例逻辑推导)。

    算法总结

  • 动态规划:用valid_sets记录各长度的合法字符集合,逐步推导更长字符串的合法性。
  • 集合与排序:通过排序字符转为固定格式(如aabcaabc排序后aabc),方便快速判断是否存在前驱。
  • 三、试题 C:LQ 图形(程序设计)

    题目

    画 L 型图形:竖线高 h、宽 w,横线高 w、宽 v+w,每个笔划由 'Q' 组成。

    解读

    分两部分绘制:

    1. 竖线:h 行,每行 w 个 'Q';
    2. 横线:w 行,每行 v+w 个 'Q'(注意横线宽度是 v+w,不是 v)。

    代码

    w, h, v = map(int, input().split())
    # 画竖线部分
    for _ in range(h):
        print('Q' * w)
    # 画横线部分
    for _ in range(w):
        print('Q' * (v + w))
    

    答案

    按输入输出样例,正确代码即可生成对应图形(无具体数值答案,需通过测试用例验证)。

    算法总结

  • 模拟绘图:拆解图形为重复单元,用循环生成每一行,核心是找规律(行数和每行字符数)。
  • 四、试题 D:最多次数(程序设计)

    题目

    统计字符串中能切割出最多的 “l、q、b” 各出现一次的子串(顺序不限),求最大次数。

    解读

    每个子串需包含 1 个 l、1 个 q、1 个 b,因此答案是三个字符出现次数的最小值。

    代码

    s = input().strip()
    l = s.count('l')
    q = s.count('q')
    b = s.count('b')
    print(min(l, q, b))
    

    答案

    如样例输入 “lqbblqblqlxqb”,输出3

    算法总结

  • 统计贪心:直接统计字符次数,取最小值,无需复杂切割逻辑,抓住问题本质(每个子串必须包含三者各一次)。
  • 五、试题 E:A・B Problem(程序设计)

    题目

    求满足内积\(X_A*X_B + Y_A*Y_B ≤ L\)的正整数向量对\((X_A,Y_A,X_B,Y_B)\)的数量。

    解读

    枚举\(X_A, Y_A\),计算内积上限后,求\(X_B, Y_B\)的可能取值。优化点:固定\(X_A, Y_A\),内积剩余值为\(L – X_A*X_B\),然后求\(Y_A*Y_B ≤ remaining\)的正整数解。

    代码

    L = int(input())
    count = 0
    for xa in range(1, L + 1):
        for ya in range(1, L + 1):
            inner = xa * xb + ya * yb  # 这里写错了,正确应为枚举xb和yb时,计算xa*xb + ya*yb ≤ L
            # 正确写法:枚举xa, ya,然后计算对于固定的xa, ya,xb和yb的可能取值
            max_xb_ya = L // xa if xa != 0 else L  # 其实应重新组织循环:
    for xa in range(1, L + 1):
        for ya in range(1, L + 1):
            remaining = L  # 内积上限是remaining
            # 对于当前xa, ya,求所有xb, yb≥1,使得 xa*xb + ya*yb ≤ remaining
            # 固定xb,求yb的最大值:yb ≤ (remaining - xa*xb)/ya,当xa*xb >= remaining时,yb无解
            for xb in range(1, L + 1):
                xb_part = xa * xb
                if xb_part >= remaining:
                    break  # yb至少1,xb_part + ya*1 > remaining时,后面的xb更大,无需继续
                max_yb = (remaining - xb_part) // ya
                if max_yb >= 1:
                    count += max_yb
                else:
                    # yb至少1,所以当(remaining - xb_part) < ya时,无解
                    pass
            # 还要考虑xb=1时,ya*yb ≤ remaining - xa*1,即yb ≤ (remaining - xa)/ya
            # 上面的循环有问题,正确做法是:枚举xa, ya, xb,计算yb的可能数量
    count = 0
    for xa in range(1, L + 1):
        for ya in range(1, L + 1):
            for xb in range(1, L + 1):
                x_part = xa * xb
                if x_part >= L:
                    break  # xb再大,x_part超过L,yb至少1,总和必超
                max_yb = (L - x_part) // ya
                if max_yb >= 1:
                    count += max_yb
    print(count)
    

    答案

    样例输入 1

    (L=2)输出 1

    样例输入 2

    (L=3)输出 5。

    算法总结

  • 三重枚举优化:通过提前 break 减少无效循环(如 xb 过大时提前终止),利用数学公式计算 yb 的可能数量,避免四重循环。
  • 六、试题 F:园艺(程序设计)

    题目

    留下最多的树,满足等间隔(间隔 d≥1)且高度严格递增。求最大保留数量。

    解读

    1. 枚举起点 i 和间隔 d,从 i 出发,每次跳 d 步,检查高度是否递增;
    2. 对每个 i,遍历所有可能的 d(d≤(n-1)/(k-1),k 为当前长度),记录最长递增序列。

    代码

    n = int(input())
    h = list(map(int, input().split()))
    max_len = 1  # 至少保留1棵树
    
    for i in range(n):
        for j in range(i+1, n):
            d_pos = j - i  # 间隔的位置差(步数)
            current_len = 2
            current_h = h[j]
            pos = j + d_pos  # 下一个位置
            while pos < n:
                if h[pos] > current_h:
                    current_len += 1
                    current_h = h[pos]
                    pos += d_pos
                else:
                    break
            max_len = max(max_len, current_len)
    
    print(max_len)
    

    答案

    样例输入 6 3 5 4 7 6 7,输出 3(选第 1、3、5 棵树,间隔 2,高度 3→4→6)。

    算法总结

  • 双重枚举:枚举起点和初始间隔,然后按间隔递增检查后续节点,时间复杂度 O (n²),对 n=5000 可接受。
  • 贪心思想:通过固定起点和间隔,贪心地寻找最长递增序列。
  • 七、试题 G:书架还原(程序设计)

    题目

    通过交换任意两本书,求最少交换次数使排列有序。

    解读

    利用循环节理论:每个循环节(如 3→1→2→3)需要长度 – 1 次交换。总次数 = 元素总数 n – 循环节数量。

    代码

    n = int(input())
    a = list(map(int, input().split()))
    visited = [False] * (n + 1)  # 元素编号1~n
    cycle_count = 0
    
    for i in range(n):
        num = a[i]
        if not visited[num]:
            cycle_count += 1
            # 遍历循环节
            current = num
            while not visited[current]:
                visited[current] = True
                current = a[current - 1]  # a是0-based,元素是1-based,正确位置是current-1
    
    print(n - cycle_count)
    

    答案

    样例输入 3 3 1 2,输出 2(循环节长度 3,3-1=2 次交换)。

    算法总结

  • 图论循环节:将排列视为有向图,每个节点指向其正确位置的元素,找出所有循环节,次数 = 节点数 – 循环节数。
  • 八、试题 H:异或和(程序设计)

    题目

    计算所有数对\((i,j)\)的\((a_i ⊕ a_j) * (j-i)\)之和,i<j。

    解读

    直接双重循环 O (n²) 会超时(n=1e5 时 1e10 次运算),需按二进制位拆分,计算每一位对总和的贡献。

    代码

    n = int(input())
    a = list(map(int, input().split()))
    ans = 0
    
    for bit in range(20):  # 最多2^20,处理0~19位
        mask = 1 << bit
        cnt0 = cnt1 = 0
        sum0 = sum1 = 0  # sum0记录0的位置和,cnt0记录0的个数,同理sum1
        for idx in range(n):
            if a[idx] & mask:
                ans += cnt0 * mask * (idx - sum0)  # 0的个数乘以当前位权,乘以位置差之和
                sum1 += idx
                cnt1 += 1
            else:
                ans += cnt1 * mask * (sum1 - idx)  # 1的个数乘以当前位权,乘以位置差之和
                sum0 += idx
                cnt0 += 1
    
    print(ans)
    

    答案

    样例输入 1

    3

    1 2 3

    输出 8;

    样例输入 2

    4

    9 8 7 6

    输出 118。

    算法总结

  • 位运算优化:逐位处理,统计该位为 1 和 0 的位置,计算每对数的位置差对总和的贡献,将时间复杂度降至 O (n*20)。
  • 总结:八道题的算法思想与核心考点

    题号 题型 核心算法 / 思想 易错点 / 关键点
    A 结果填空 模拟、条件分支 回合数从 1 开始,余数判断正确
    B 结果填空 动态规划、集合排序 字符排序后检查前驱集合,字典序处理
    C 程序设计 模拟绘图、规律拆解 横线宽度是 v+w,不是 v
    D 程序设计 统计贪心 直接取三字符次数最小值,无需切割
    E 程序设计 枚举优化、数学推导 三重循环优化,避免无效计算
    F 程序设计 双重枚举、贪心 间隔是位置差,不是步数,递增条件严格
    G 程序设计 循环节理论、图论 正确转换 1-based 元素与 0-based 索引
    H 程序设计 位运算、逐位统计 按二进制位拆分,计算每一位的贡献

    参赛建议

    1. 先易后难:优先解决填空和简单模拟题(如 A、C、D),确保基础分拿到手。
    2. 注意细节:输入输出格式、边界条件(如试题 E 中 xb=0 的情况不存在,因为是正整数)、索引转换(试题 G 的 1-based 元素)。
    3. 算法优化:遇到 O (n²) 可能超时的题(如 F、H),及时想到枚举优化(如试题 F 固定起点和间隔)或数学方法(如试题 H 的位运算)。

    第一次参赛难免紧张,但通过逐题拆解会发现,蓝桥杯的核心还是考察基础算法的灵活运用。从模拟到贪心,从枚举到动态规划,每道题都是对编程思维的打磨。记住:代码不在复杂,正确和高效才是关键!

    作者:我不是秋秋

    物联沃分享整理
    物联沃-IOTWORD物联网 » 第十六届蓝桥杯Python大学B组试题详解:全方位解析A至H题解题思路与代码实现

    发表回复