技巧---折半搜索

1 篇文章 0 订阅
1 篇文章 0 订阅

前言

算法多样化是重要的,更重要的是算法最优化。

                                                                                                                        ---邱学华

        搜索是一种布鲁特佛斯(Brute-Force)算法,相当于一种枚举,所以来说,他的运行效率十分之慢,大部分情况下,如果数据范围较大,则必须使用各种复杂的剪枝来加快运行速度,而如果对于一个用搜索可以很轻松的做出来,但是时间复杂度超标的题目,如果没有可行剪枝方案,该如何继续用搜索完成呢?这就要用到我们今天讲的折半搜索来实现了(非二分查找),折半搜索是一种优化dfs算法,可以大幅减少所消耗的时间。

目录

前言

目录

正文

-折半搜索的思想

-折半搜索的实现

T1.世界冰球锦标赛

T2.XOR


正文

-折半搜索的思想

        对于一个数组来说,如果我们要对其使用深度优先搜索(dfs)求出所要求的答案,假设每个数都有两种状态,选择和不选择,那么时间复杂度是非常高的,因为其达到了O(2^{n}),那么很明显,这只能处理数据量非常小的数据,甚至达到了n\leq 27,这显然无法处理大部分数据,如果已经没有任何剪枝的办法,该怎么优化呢?

        这就要用到折半搜索了,折半搜索的思想通俗意义上来讲就是把整个数组“劈成两半”,对于左右两边分别进行求解(如图),在最后再放在一起统计可行解的数量,这样的话,时间复杂度就被优化到了O(\sqrt{2^{n}}),这比原来快了显然不止一点半点,甚至比大部分剪枝更加有效果,所以,可以用这个方法来解决很多题目。

注意:折半搜索和二分查找在思想上只有第一次取中点有关系,其余完全无关!

-折半搜索的实现

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;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值