例题 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/ |
| 类型 | 贪心 + 排序 + 双指针 |
| 核心 | 最小配最大 |
题目描述
纪念品价格上限 w,n 个纪念品,每组最多两件,价格之和 <= 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。如果先 q 后 p:
- 累加过程:
q, q+p - 如果
q < 0,可能第一步就失败
如果先 p 后 q:
- 累加过程:
p, p+q p更大,更容易先大于0,后续更稳定
结论:按贡献从大到小排序,能让累加和尽快突破0,且最稳定。
📊 今日刷题总结
| 题号/来源 | 考点 | 贪心类型 | 难度 | 核心技巧 |
|---|---|---|---|---|
| 数字统计 | 暴力枚举 | - | ⭐ | 字符串统计 |
| 接水问题 | 堆+贪心 | 优先队列 | ⭐⭐⭐ | 每次选最快完成的龙头 |
| 小蓝的零花钱 | 排序+贪心 | 排序后从小到大选 | ⭐⭐⭐ | 先找合法点,再按代价选 |
| P545 | 石子合并 | 堆 | ⭐⭐⭐ | 每次取最小两个 |
| P532 | 分箱问题 | 排序+双指针 | ⭐⭐ | 最小配最大 |
| P209 | 翻硬币 | 从左往右扫 | ⭐⭐ | 遇到不同就翻 |
| 三国游戏 | 排序+贪心 | 贡献大的先发生 | ⭐⭐⭐⭐ | 控制事件顺序,让累加和尽快>0 |