备战蓝桥杯国赛【Day 8】

例题 1:数字统计(蓝桥杯基础题)

项目 内容
类型 暴力枚举 / 数学
核心 遍历区间,统计数字出现次数

题目描述

统计范围 [L, R] 的所有整数中,数字 2 出现的次数。

输入格式

复制代码
L R

输出格式

数字 2 出现的次数。

题解

直接遍历每个数,转成字符串统计 2 的个数。

python 复制代码
l, r = map(int, input().split())
ans = 0
for i in range(l, r + 1):
    ans += str(i).count('2')
print(ans)

推演验证

复制代码
输入: 2 22

i=2: "2" → 1个2, ans=1
i=3~11: 无2, ans=1
i=12: "12" → 1个2, ans=2
i=20: "20" → 1个2, ans=3
i=21: "21" → 1个2, ans=4
i=22: "22" → 2个2, ans=6

输出: 6 ✓

复杂度分析

指标 复杂度 说明
时间 O((R-L) * log R) 遍历区间,每个数转字符串
空间 O(1) 只维护计数器

优化思路

数据范围 R <= 10^4,暴力即可。如果 R <= 10^18,需要用数位DP


例题 2:接水问题(蓝桥杯经典题)

项目 内容
类型 贪心 + 堆(优先队列)
核心 每次选最快接完水的龙头,分配下一个人

题目描述

m 个水龙头,n 个人接水,每人接水量 w[i]。每人每秒接水量为1。求所有人接完水的最短时间。

输入格式

复制代码
n m
w1 w2 ... wn

输出格式

最短时间(秒)。

题解

贪心策略:用堆维护每个龙头的剩余接水量,每次选接完最早的龙头,分配下一个人。

python 复制代码
import heapq

n, m = map(int, input().split())
w = list(map(int, input().split()))

# 初始化m个龙头,前m个人直接分配
long = w[:m]
heapq.heapify(long)

i = m  # 下一个待分配的人的索引

while i < n:
    # 取出最快接完的龙头
    minn = heapq.heappop(long)
    # 分配下一个人,该龙头累计接水量增加
    heapq.heappush(long, minn + w[i])
    i += 1

# 最后一个接完的时间就是堆中的最大值
print(max(long))

推演验证

复制代码
输入: 5 3
      4 4 1 2 1

初始化: long = [4, 4, 1] (堆化后: [1, 4, 4])

i=3, w[3]=2:
  pop最小=1, push 1+2=3, long=[3, 4, 4]
  
i=4, w[4]=1:
  pop最小=3, push 3+1=4, long=[4, 4, 4]

结束, max(long)=4

输出: 4 ✓

复杂度分析

指标 复杂度 说明
时间 O(n log m) 堆操作 O(log m),共 n
空间 O(m) 堆的大小

关键细节

坑点 说明
堆存的是累计接水量 不是单个人的接水量,是龙头上所有人的总和
max(long) 取结果 堆顶是最小,但答案是最后一个完成的,即堆中最大
前m个人直接入堆 不需要比较,直接分配

例题 3:小蓝的零花钱(蓝桥杯真题)

项目 内容
类型 贪心 + 排序
核心 先找所有合法切割点,再按代价从小到大选

题目描述

长度为 n 的序列,偶数和奇数数量相等。每次操作选一个位置切割,要求两段中偶数和奇数数量都相等。代价为切割两端元素的差的绝对值。预算 B,求最多操作次数。

输入格式

复制代码
n B
a1 a2 ... an

输出格式

最多操作次数。

题解

步骤1 :找所有合法切割点。遍历每个位置,统计左边偶数和奇数数量,当 偶数==奇数 时,该位置可切割。

步骤2:收集所有合法切割的代价,排序。

步骤3:从小到大选,直到预算不够。

python 复制代码
n, b = map(int, input().split())
a = list(map(int, input().split()))

cnt = 0      # 当前偶数-奇数的差值
res = []     # 所有合法切割的代价

# 遍历每个可能的切割位置(0到n-2)
for i in range(n - 1):
    if a[i] % 2 == 0:
        cnt += 1
    else:
        cnt -= 1
    
    # 如果cnt==0,说明[0..i]中偶数=奇数,可以切割
    if cnt == 0:
        # 代价为切割两端元素的差的绝对值
        res.append(abs(a[i] - a[i + 1]))

# 贪心:按代价从小到大选
res.sort()

ans = 0
for cost in res:
    if b >= cost:
        b -= cost
        ans += 1
    else:
        break

print(ans)

推演验证

复制代码
输入: 6 3
      1 2 3 4 5 6

i=0: a[0]=1(奇), cnt=-1, 不为0
i=1: a[1]=2(偶), cnt=0, 合法! 代价=|2-3|=1, res=[1]
i=2: a[2]=3(奇), cnt=-1, 不为0
i=3: a[3]=4(偶), cnt=0, 合法! 代价=|4-5|=1, res=[1,1]
i=4: a[4]=5(奇), cnt=-1, 不为0

res=[1,1], 排序后=[1,1]

b=3, 选第一个: b=2, ans=1
选第二个: b=1, ans=2

输出: 2 ✓

复杂度分析

指标 复杂度 说明
时间 O(n log n) 遍历 O(n) + 排序 O(n log n)
空间 O(n) 存储代价数组

关键细节

坑点 说明
cnt 的含义 偶数个数 - 奇数个数,为0时平衡
只遍历到 n-2 最后一段需要至少一个元素
代价是 abs(a[i]-a[i+1]) 切割位置的两端元素
贪心排序后从小到大选 预算有限,先选代价小的

例题 4:石子合并(蓝桥杯 P545)

项目 内容
链接 https://www.lanqiao.cn/problems/545/learning/
类型 贪心 + 堆
核心 每次选最小的两个合并

题目描述

n 个部落,人数 t[i]。每次合并两个部落,花费为人数之和。求合并成一个部落的最小总花费。

题解

python 复制代码
import heapq

n = int(input())
a = list(map(int, input().split()))

heapq.heapify(a)

ans = 0
while len(a) >= 2:
    x = heapq.heappop(a)
    y = heapq.heappop(a)
    cost = x + y
    ans += cost
    heapq.heappush(a, cost)

print(ans)

推演验证

复制代码
n=4, a=[1,2,3,4]

堆: [1,2,3,4]

第1轮: 取1,2, 合并=3, ans=3, 堆=[3,3,4]
第2轮: 取3,3, 合并=6, ans=9, 堆=[4,6]
第3轮: 取4,6, 合并=10, ans=19, 堆=[10]

输出: 19 ✓

例题 5:分箱问题(蓝桥杯 P532)

项目 内容
链接 https://www.lanqiao.cn/problems/532/learning/
类型 贪心 + 排序 + 双指针
核心 最小配最大

题目描述

纪念品价格上限 wn 个纪念品,每组最多两件,价格之和 <= w。求最少分组数。

题解

python 复制代码
w = int(input())
n = int(input())
a = [int(input()) for _ in range(n)]

a.sort()

l, r = 0, n - 1
ans = 0

while True:
    if l == r:
        ans += 1
        break
    if l > r:
        break
    
    if a[l] + a[r] <= w:
        ans += 1
        l += 1
        r -= 1
    else:
        ans += 1
        r -= 1

print(ans)

例题 6:翻硬币(蓝桥杯 P209)

项目 内容
链接 https://www.lanqiao.cn/problems/209/learning/
类型 贪心 + 从左往右扫
核心 遇到不同就翻

题目描述

初始状态 s,目标状态 t,每次翻转相邻两个硬币。求最少操作次数。

题解

python 复制代码
s = list(input())
t = list(input())
n = len(s)

ans = 0
for i in range(n - 1):
    if s[i] == t[i]:
        continue
    
    # 翻转 i 和 i+1
    s[i] = t[i]
    s[i + 1] = '*' if s[i + 1] == 'o' else 'o'
    ans += 1

print(ans)

例题 7:三国游戏(蓝桥杯真题)------ 重点变形题

项目 内容
类型 贪心 + 排序
核心 控制事件顺序,让贡献最大的事件先发生

题目描述

魏(X)、蜀(Y)、吴(Z)三个国家,初始士兵为0。n 个事件,每个事件让 X,Y,Z 分别增加 A[i], B[i], C[i]。游戏结束时,若某国士兵 > 另外两国之和,则获胜。求最多发生多少个事件能让某国获胜。若无法获胜输出 -1

关键观察

获胜条件X > Y + Z 等价于 X - Y - Z > 0

贪心策略:对于某个国家获胜的情况,计算每个事件的"贡献值":

  • 若想让 X 获胜:贡献 = A[i] - B[i] - C[i]
  • 若想让 Y 获胜:贡献 = B[i] - A[i] - C[i]
  • 若想让 Z 获胜:贡献 = C[i] - A[i] - B[i]

核心洞察:事件可以控制顺序!让贡献大的事件先发生,这样累加和能尽快大于0,且不会中途掉下去。

为什么排序有效?

  • 如果先发生贡献小的事件,累加和可能一直小于0
  • 如果先发生贡献大的事件,累加和尽快大于0,后面即使有小贡献也能维持

完整代码

python 复制代码
n = int(input())
A = list(map(int, input().split()))
B = list(map(int, input().split()))
C = list(map(int, input().split()))

def win(X, Y, Z):
    """
    计算第一个国家获胜时,最多能发生多少个事件
    X: 获胜国家的增量数组
    Y, Z: 另外两个国家的增量数组
    """
    w = 0  # 当前累计贡献
    contribute = []  # 每个事件对获胜的贡献值
    
    for i in range(n):
        # 事件i发生后,X增加X[i],Y和Z增加Y[i]和Z[i]
        # 对X获胜的贡献 = X[i] - Y[i] - Z[i]
        contribute.append(X[i] - Y[i] - Z[i])
    
    # 贪心:贡献大的事件先发生,让累加和尽快大于0
    contribute.sort(reverse=True)
    
    for i in range(n):
        w += contribute[i]
        # 如果加入第i个事件后,累加和<=0,说明前i个事件无法维持获胜
        # 返回i(因为range从0开始,i就是前面成功的事件数)
        if w <= 0:
            return i
    
    # 所有事件都发生,累加和始终>0
    return n

# 分别计算三个国家获胜的情况,取最大值
result = max(win(A, B, C), win(B, A, C), win(C, A, B))

if result == 0:
    print(-1)  # 无法让任何国家获胜
else:
    print(result)

推演验证

复制代码
输入: 3
      1 2 3
      2 3 2
      1 0 7

事件0: A=1, B=2, C=1 → X贡献=1-2-1=-2, Y贡献=2-1-1=0, Z贡献=1-2-1=-2
事件1: A=2, B=3, C=0 → X贡献=2-3-0=-1, Y贡献=3-2-0=1, Z贡献=0-2-3=-5
事件2: A=3, B=2, C=7 → X贡献=3-2-7=-6, Y贡献=2-3-7=-8, Z贡献=7-3-2=2

win(A,B,C): 贡献=[-2,-1,-6], 排序=[-1,-2,-6]
  i=0: w=-1<=0, 返回0
  
win(B,A,C): 贡献=[0,1,-8], 排序=[1,0,-8]
  i=0: w=1>0
  i=1: w=1+0=1>0
  i=2: w=1+0-8=-7<=0, 返回2
  
win(C,A,B): 贡献=[-2,-5,2], 排序=[2,-2,-5]
  i=0: w=2>0
  i=1: w=2-2=0<=0, 返回1

result = max(0, 2, 1) = 2

输出: 2 ✓

复杂度分析

指标 复杂度 说明
时间 O(n log n) 排序 O(n log n),三次调用
空间 O(n) 贡献值数组

关键细节

坑点 说明
事件顺序可控 这是本题的关键,可以任意排序事件
贡献值可能为负 负贡献的事件放在后面,前面先用正贡献垫高
返回i的含义 第i个事件加入后首次<=0,说明前i个成功
result==0输出-1 无法让任何国家获胜

贪心正确性证明

为什么贡献大的先发生最优?

假设有两个事件,贡献分别为 p > q。如果先 qp

  • 累加过程:q, q+p
  • 如果 q < 0,可能第一步就失败

如果先 pq

  • 累加过程:p, p+q
  • p 更大,更容易先大于0,后续更稳定

结论:按贡献从大到小排序,能让累加和尽快突破0,且最稳定。


📊 今日刷题总结

题号/来源 考点 贪心类型 难度 核心技巧
数字统计 暴力枚举 - 字符串统计
接水问题 堆+贪心 优先队列 ⭐⭐⭐ 每次选最快完成的龙头
小蓝的零花钱 排序+贪心 排序后从小到大选 ⭐⭐⭐ 先找合法点,再按代价选
P545 石子合并 ⭐⭐⭐ 每次取最小两个
P532 分箱问题 排序+双指针 ⭐⭐ 最小配最大
P209 翻硬币 从左往右扫 ⭐⭐ 遇到不同就翻
三国游戏 排序+贪心 贡献大的先发生 ⭐⭐⭐⭐ 控制事件顺序,让累加和尽快>0
相关推荐
智者知已应修善业1 小时前
【51单片机模拟生日蜡烛】2023-10-10
c++·经验分享·笔记·算法·51单片机
MediaTea1 小时前
Scikit-learn:从数据到结构——无监督学习的最小闭环
人工智能·学习·算法·机器学习·scikit-learn
智者知已应修善业2 小时前
【51单片机如何让LED灯从一亮到八,再从八亮到一】2023-10-13
c++·经验分享·笔记·算法·51单片机
qeen872 小时前
【数据结构】二叉树相关经典函数C语言实现
c语言·数据结构·c++·笔记·学习·算法·二叉树
良木生香2 小时前
【C++初阶】STL——List从入门到应用完全指南(1)
开发语言·数据结构·c++·程序人生·算法·蓝桥杯·学习方法
WL_Aurora2 小时前
【每日一题】贪心
python·算法
aqiu1111113 小时前
【并查集专题top】
c++·算法
叼烟扛炮3 小时前
C++ 知识点17 友元
开发语言·c++·算法·友员
richard_yuu3 小时前
数据结构|二叉树高阶进阶-经典算法
数据结构·c++·算法