折半搜索 学习笔记
关于算法
折半搜索,又称 meet in the middle 算法。
顾名思义,就是将整个搜索的过程分成两个部分分别进行搜索,然后再将两个部分搜索出来的答案进行合并,得到最终的答案。
dfs 搜索算法一般都是指数级别的,那么我们假如每次 dfs 时都有两种决策,那么我们执行 dfs 算法的时间复杂度为
我们来看一下下面两张图。
很容易发现,左边这一张图是普通的只执行
显然,左边这一个搜索树随着层数的增加,每一层的节点数成指数级增长,最终会在最后一层的某一个节点上产生答案。(假设黄色部分表示最终答案。)
而右边的折半搜索产生的搜索树,恰巧就可以避免随着层数的不断增加导致节点数大规模增长,当第二次搜索完毕之后,和第一次搜索出来的结果进行合并操作,得到最终的答案。
那么这个时候我们的折半搜索的时间复杂度就会优化为
例题
这道题乍一看可以用背包做,但
但,
以
对于折半搜索来讲,难点一般都在如何合并上,对于这一道题,显然,当我们后一半搜索完毕后,我们可以对前一半的所有结果(前一半每种方案所花的钱)进行从小到大排序,再二分查找,我们看前一半中的方案所花的钱,加上当前花的钱,超不超过
代码实现:
#include <bits/stdc++.h>
#define int long long
const int N = 105;
using namespace std;
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-'){
w = -1;
}
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
int n,m,arr[N],half,cnt = 0,v[1 << 24];
int ans = 0;
void dfs1(int pos,int s){
if (pos == half + 1){
cnt++;
v[cnt] = s;
return;
}
if (1ll * s + 1ll * arr[pos] <= m){
dfs1(pos + 1,1ll * s + 1ll * arr[pos]);
}
dfs1(pos + 1,s);
}
void dfs2(int pos,int s){
if (pos == n + 1){ // 合并。
ans += upper_bound(v + 1,v + cnt + 1,m - s) - v - 1; // 二分查找。
return;
}
if (1ll * s + 1ll * arr[pos] <= m){
dfs2(pos + 1,1ll * s + 1ll * arr[pos]);
}
dfs2(pos + 1,s);
}
signed main(){
n = read(),m = read();
for (int i = 1;i <= n;i++){
arr[i] = read();
}
half = n >> 1;
dfs1(1,0);
sort(v + 1,v + cnt + 1);
dfs2(half + 1,0);
printf("%lld",ans);
return 0;
}
更多练习:
CF1006F Xor-Paths
CF888E Maximum Subsequence
AcWing 171.送礼物
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】