进入十一月,青少儿编程赛事进入密集备考期,几大标志性比赛的时间节点都摆在这里:
第十八届蓝桥杯软件赛 (C/C++ 程序设计、Python 程序设计均为核心科目)报名已于 2026-10-08 10:00 开启,至 2026-12-18 14:00 截止;省赛 / 全国选拔赛定于 2027 年 4 月 (4 月 10、11 日),全国总决赛 2027 年 6 月中上旬 (dasai.lanqiao.cn 通知 3004 已证实)。
中国电子学会 2026 年 12 月青少年软件编程等级考试 (C/C++ 一级至十级、Python 一级至六级)报名 2026-09-23 --- 12-02 16:00 ,全国测试 12-10 19:00--21:00 ,统考 12-12 (C/C++ 8:30--10:30 / Python 14:00--15:00)(qceit.org.cn)。
全国青少年信息素养大赛 2026---2027 赛季算法应用主题赛,大纲明确包含搜索、枚举与基础算法。
三个赛事共同高频出现一类题:"从若干物品里选一部分,使某指标恰好等于目标"。当物品数量偏多、纯暴力会超时,却还没到动态规划能解的程度时,折半搜索(Meet in the Middle) 就是那把打开局面的钥匙。

一、题目 / 项目目标
我们把问题包装成一道校园场景题------「校园能量币密码验证」:
有 n 枚能量币,第 i 枚面值为
a[i](正整数)。社团想验证:能否从中选出若干枚(每枚选或不选),使选出能量币的总面值恰好等于目标能量 T? 若能,共有多少种不同选法?数据范围:
1 ≤ n ≤ 40,1 ≤ a[i] ≤ 10^9,1 ≤ T ≤ 10^15。
为什么这道题值得讲? 因为 n 最大到 40,全选/不选的暴力枚举有 2^40 ≈ 1e12 种情况,绝对超时;而 a[i] 和 T 又大到没法用普通背包 DP(O(n·T) 直接爆掉)。这正是折半搜索的"黄金区间":把 40 拆成 20+20,每半只枚举 2^20 ≈ 1e6 次。
样例 :a = [2, 3, 5, 7],T = 10。
总面值恰为 10 的选法有两种:{3, 7} 和 {2, 3, 5},所以答案为 可行,方案数 = 2。
二、核心考点
- 折半搜索(Meet in the Middle)思想:把大问题对半拆,分别暴力,再用哈希表"拼"起来。
- 位运算枚举子集 :用
mask的每一位表示"选 / 不选"第 i 个物品。 - 哈希表计数:前半所有子集和 → 出现次数;后半查"互补值"是否存在、有多少。
- 数据类型与边界:大数用 64 位整数(或语言自带大整数),避免溢出。
- 复杂度判断:能区分"该用折半还是该用 DP",是蓝桥杯选对算法的关键。
三、解法 / 拆解
3.1 原理
- 把 n 枚能量币从中间
mid = n / 2切成两半。 - 枚举前半 :遍历前
mid个物品的所有2^mid种选 / 不选组合,算出每个组合的子集和s,用哈希表left[s]记录"和为 s 的组合出现了几次"。 - 枚举后半 :遍历后
n - mid个物品的2^(n-mid)种组合,算出子集和s。要让总面值等于 T,前半需要补上T - s。直接查left[T - s]有多少,累加到答案。
整体时间复杂度从 O(2^n) 降到 O(2^(n/2)) ,n=40 时约 2^20 ≈ 1e6 次操作,轻松通过。
3.2 C++ 实现
cpp
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
using ll = long long;
// 折半搜索:返回"选若干能量币使总和恰为 T"的方案数
// a: 能量币面值,T: 目标能量
ll meetInMiddle(const vector<ll>& a, ll T) {
int n = a.size();
int mid = n / 2;
unordered_map<ll, ll> left; // 前半部分所有子集和 -> 出现次数
// 枚举前半部分 2^mid 种选法
for (int mask = 0; mask < (1 << mid); ++mask) {
ll s = 0;
for (int i = 0; i < mid; ++i)
if (mask & (1 << i)) s += a[i];
left[s]++;
}
ll ans = 0;
int r = n - mid;
// 枚举后半部分 2^r 种选法
for (int mask = 0; mask < (1 << r); ++mask) {
ll s = 0;
for (int i = 0; i < r; ++i)
if (mask & (1 << i)) s += a[mid + i];
// 后半和为 s,需要前半补上 T - s
auto it = left.find(T - s);
if (it != left.end()) ans += it->second;
}
return ans;
}
int main() {
vector<ll> a = {2, 3, 5, 7};
ll T = 10;
ll ways = meetInMiddle(a, T);
cout << (ways > 0 ? "可行" : "不可行")
<< ",方案数 = " << ways << "\n"; // 输出:可行,方案数 = 2
return 0;
}
3.3 Python 实现
python
from collections import defaultdict
def meet_in_middle(a, T):
n = len(a)
mid = n // 2
left = defaultdict(int)
# 枚举前半部分 2^mid 种选法
for mask in range(1 << mid):
s = 0
for i in range(mid):
if mask & (1 << i):
s += a[i]
left[s] += 1
ans = 0
r = n - mid
# 枚举后半部分 2^r 种选法
for mask in range(1 << r):
s = 0
for i in range(r):
if mask & (1 << i):
s += a[mid + i]
ans += left.get(T - s, 0)
return ans
a = [2, 3, 5, 7]
T = 10
ways = meet_in_middle(a, T)
print("可行" if ways > 0 else "不可行", "方案数 =", ways) # 可行 方案数 = 2
3.4 时间与空间复杂度
- 时间 :枚举前半
2^(n/2)+ 后半2^(n-mid),每次哈希查询O(1),总体O(2^(n/2))。 - 空间 :
left哈希表最多2^(n/2)个键,O(2^(n/2))。 - n=40 时约
2^20 ≈ 1e6,内存与耗时都在安全范围内,远优于纯暴力2^40。
四、七大易错点
- 子集枚举的循环边界 :枚举掩码要写
mask < (1 << mid),即共2^mid个(含空集 0)。写成mask <= (1 << mid)会越界多算;写成i <= n会把后半漏掉。 - 分割点下标算错 :
mid = n / 2,第二半必须从a[mid]开始、长度为n - mid。常见错误是把a[mid]既算进前半又算进后半,或前后两半都没覆盖它。 - 只存"是否存在"而丢次数 :
left必须记出现次数,若只存布尔,多个子集和相同的情况会被合并成 1,方案数被严重低估。 - 整数溢出 :
a[i] ≤ 1e9,40 个全选总和可达4e10,远超 32 位int。C++ 必须用long long,Python 虽自动大整数但字典键也要能容纳负数(T - s可能为负)。 - 负数键未处理 :当后半和
s > T时,T - s为负。Python / C++unordered_map都支持负数键(返回 0 或find失败),但若自行用数组下标就需整体偏移,否则越界。 - 重复面值是否去重 :题目按"第几枚能量币"区分选法,相同面值的不同币算不同方案;若题意要"按面值去重"则须改算组合而非排列------动手前先确认题意。
- 多测未清空字典 :若在多组数据上反复调用,必须每次重建
left,否则会累积上一轮的子集和,得到错误答案。
五、进阶
- 双向 BFS:和折半同源,都是"从两端往中间对半扩展"------状态空间搜索时从起点与终点同时扩展,相遇即合并。
- 迭代加深搜索(IDDFS):当解的深度未知、纯 DFS 易爆栈时,用"限制深度、逐步加深"替代盲目深搜。
- 折半的适用边界 :它适合"每个元素二选一 / 分支有限、且
2^(n/2)可接受"的场景;若 n 大到 50,需改用排序 + 双指针去重、或母函数 / DP。 - 折半 vs 背包 DP :若
a[i]、T都较小(如T ≤ 1e4),布尔背包O(n·T)更直观;本题T高达1e15,DP 直接不可用------判断该用哪种算法,正是蓝桥杯拉开差距的地方。 - 输出具体方案 :
left不只记次数,也记录到达该和的mask,合并时把前后mask拼回原数组下标,即可还原"选了哪几枚能量币"。
六、小结与互动
折半搜索把"指数级暴力"拦腰砍半,用哈希表在中间会师,是蓝桥杯、电子学会考级与信息素养大赛里处理"选 / 不选"大类题型的利器。记住它的适用信号:分支少、总数中等、目标值大、DP 不可行。
你还在哪道题上用过"对半拆开再合并"的思路?是双向 BFS、还是别的搜索优化?欢迎在评论区聊聊你遇到的同类题,我们一起拆解。
(本文为原创算法解析,转载请附出处。关注获取更多蓝桥杯 / 信息素养 / 电子学会考级备考干货。)
📚 免费少儿编程资料(夸克网盘领取)
以下资料来自夸克网盘分享,点击链接可直接保存;若需在 App 内打开,也可复制下方明文链接:
- 全国青少年信息素养大赛复赛集训题目Python&C++.docx
https://pan.quark.cn/s/93995d3cb150 - 2024信息素养-智能算法应用挑战赛-复赛初中组题目7月7日.pdf
https://pan.quark.cn/s/da97b5dbf75d - Python背记手册.pdf
https://pan.quark.cn/s/7568ae9ca92b - Python课程
https://pan.quark.cn/s/a94bf02d00c6 - 2024信息素养大赛图形化复赛集训题答案3-9
https://pan.quark.cn/s/6ccab7ec3cbc - 2025年03月份电子学会考级真题
https://pan.quark.cn/s/4403c4228912 - 2025全国青少年信息素养大赛赛项说明
https://pan.quark.cn/s/d9d0df4a9f29 - 青少儿信息素养大赛编程资料
https://pan.quark.cn/s/4ab6bd83be8a
资料持续更新,关注获取最新分享。