前言
算法多样化是重要的,更重要的是算法最优化。
---邱学华
搜索是一种布鲁特佛斯(Brute-Force)算法,相当于一种枚举,所以来说,他的运行效率十分之慢,大部分情况下,如果数据范围较大,则必须使用各种复杂的剪枝来加快运行速度,而如果对于一个用搜索可以很轻松的做出来,但是时间复杂度超标的题目,如果没有可行剪枝方案,该如何继续用搜索完成呢?这就要用到我们今天讲的折半搜索来实现了(非二分查找),折半搜索是一种优化dfs算法,可以大幅减少所消耗的时间。
目录
正文
-折半搜索的思想
对于一个数组来说,如果我们要对其使用深度优先搜索(dfs)求出所要求的答案,假设每个数都有两种状态,选择和不选择,那么时间复杂度是非常高的,因为其达到了O(),那么很明显,这只能处理数据量非常小的数据,甚至达到了,这显然无法处理大部分数据,如果已经没有任何剪枝的办法,该怎么优化呢?
这就要用到折半搜索了,折半搜索的思想通俗意义上来讲就是把整个数组“劈成两半”,对于左右两边分别进行求解(如图),在最后再放在一起统计可行解的数量,这样的话,时间复杂度就被优化到了O(),这比原来快了显然不止一点半点,甚至比大部分剪枝更加有效果,所以,可以用这个方法来解决很多题目。
注意:折半搜索和二分查找在思想上只有第一次取中点有关系,其余完全无关!
-折半搜索的实现
T1.世界冰球锦标赛
题面概括:
给定n场比赛门票的价格,求出再不超过预算m的情况下,有多少种观赛方案(不看也算一种)
思路:
这道题很容易想到深搜,但是时间复杂度太高了,也没有什么剪枝方案,所以就需要用到折半搜索,然后记录下来每种可能可行的情况,最后再二分计算可行数即可。
代码:
#include<bits/stdc++.h>
using namespace std;
long long m,a[55],n;
vector<long long>s,b;
void dfs(int l,int r,long long sum,vector<long long>&k){
if(sum>m){
return ;
}
if(l>r){
k.push_back(sum);
return ;
}
dfs(l+1,r,sum+a[l],k);
dfs(l+1,r,sum,k);
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
dfs(1,n/2,0,s);
dfs(n/2+1,n,0,b);
long long sum=0;
sort(s.begin(),s.end());
for(int i=0;i<b.size();i++){
sum+=upper_bound(s.begin(),s.end(),m-b[i])-s.begin();
}
cout<<sum;
return 0;
}
T2.XOR
题面概括:
给出一个 n×m 的网格,每个格子上有权值a[i][j],要从 (1,1)走到 (n,m),每次只能向右或向下走,计算路径上的权值的异或和,求异或和等于k的路径数。
思路:
这道题还是很容易想到深搜并折半搜索,但是这种二维矩阵该怎么进行折半搜索呢?我们考虑一下,对于n×m的矩阵,从左上角走到右下角总共需要n+m-2步,那么就可以考虑把步数分为前一半和后一半,然后查找交点,但是,当然不可能遍历所有的交点,所以我们考虑在第一次搜索时把各种情况用map记录下来,因为异或是自己的逆运算,所以第二次搜索时遍历到每个终点后,如果异或值为x,直接累加第一次记录的在同一交点,且异或值为k^x的就可以了。
代码:
#include<bits/stdc++.h>
using namespace std;
long long a[30][30],k;
long long ans;
int t;
int n,m;
map<pair<pair<int,int>,long long>,long long> ma;
void dfs1(long long now,int x,int y,int step){
now^=a[x][y];
if(step==t){
ma[make_pair(make_pair(x,y),now)]++;
return;
}
if(x+1<=n) dfs1(now,x+1,y,step+1);
if(y+1<=n) dfs1(now,x,y+1,step+1);
}
void dfs2(long long now,int x,int y,int step){
if(step==t){
ans+=ma[make_pair(make_pair(x,y),k^now)];
return;
}
if(x-1>=1) dfs2(now^a[x][y],x-1,y,step+1);
if(y-1>=1) dfs2(now^a[x][y],x,y-1,step+1);
}
int main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
t=(n+m-2)/2;
dfs1(0,1,1,0);
t=(n+m-1)/2;
dfs2(0,n,m,0);
cout<<ans;
return 0;
}