关于蓝桥杯之前的总结 搜索基础篇

小伙伴们肯定会一种搜索,暴力循环搜索。
for(int i=0;i<n;i++)
这里我想试问,为什么这样能实现搜索呢?状态 i ->状态 i+1 实现的是什么?
显然 i -> i+1 是因为你通过不断的加能全部读取这个数组。同样的我们也可以先从最大的到最小的,或者先查偶数下标,再查奇数下标… 无论用什么方式,我们都能确定能走完所有情况。

这就确立了搜索的一个特性,结果完备性

同时极大多数时候我们处理一个问题时只需要搜索同一个位置一次。

这确立了搜索的一个特性,无重复性。

基于这两种特性,我们可以其他的搜索方式

1.广度&深度优先搜索
我们所了解的暴力循环搜索通常是基于某种规则从一个状态到另外一个状态
在这里插入图片描述
那我们可不可以实现基于莫一种规则从一个状态可以到达另外几个状态?
在这里插入图片描述

譬如题目中告诉我我从位置(5,5)可以上下左右走

在这里插入图片描述可以实现吗?当然可以。但这与我们之前的状态i->状态i+1不一样,这个时候的i+1状态如何存取?

实现存取的方式多种多样!小伙伴可以脑洞大开,自己设计存取方法。我们通常使用的是一种常见的数据结构队列。我们从把i+1状态的所有点依次入队列,下次直接从队列中将点取出来挨个看他的下个状态,做重复操作,这样实现了搜索所有状态。

当然为了避免与搜索的无重复性冲突,我们需要记录那些状态我们去过了,去过了就不去了。没意义了。

到这里相信大家对此类搜索有了一个基本的概念。
但是它为什么叫广度优先搜索呢?
这里我们就要聊到队列先进先出的特性了。
以上面的例子为例,我从i状态衍生出了四个点之后。我一定是先把i+1状态所有的点都看过一遍再去看下一层的点
在这里插入图片描述
即这个第三层的点一定是最后才看,因为我们第二层的点比第三层的先进队列。因此它叫广度优先队列。一定会把本层的点全部看完,才会去看下一层的点。

上代码:

#include<bits/stdc++.h>
#define Pair pair<int,int>
using namespace std;
const int N=10000+10;

bool vis[N][N];//标记该状态是否为第一次到达
queue<Pair> que;
int Ca,Cb,Ct;

void Push(Pair Cur){
	if(Cur.first==Ct||Cur.second==Ct||Cur.first+Cur.second==Ct){
		cout<<"True!";
		exit(0);
	}
	if(vis[Cur.first][Cur.second]) return;
	que.push(Cur);
	vis[Cur.first][Cur.second]=1;
}

void BFS(Pair now){
	memset(vis,false,sizeof(vis));
	Pair Cur=now;
	que.push(now);
	while(!que.empty()){
		now=que.front();
		que.pop();
		
		Cur=now;
		Cur.first=0;//A水桶倒空
		Push(Cur);
		
		Cur=now;
		Cur.second=0;//B水桶倒空
		Push(Cur);
		
		Cur=now;
		Cur.first=Ca;//A水桶倒满
		Push(Cur);
		
		Cur=now;
		Cur.second=Cb;//B水桶倒满
		Push(Cur);
		
		Cur=now;//A水桶向B水桶倒水
		if(Cur.first+Cur.second>Cb){
			Cur.second=Cb;
			Cur.first=Cur.first+Cur.second-Cb;
		}
		else{
			Cur.first=0;
			Cur.second=Cur.first+Cur.second;
		}
		Push(Cur);
		
		
		Cur=now;//B水桶向A水桶倒水
		if(Cur.first+Cur.second>Ca){ 
			Cur.first=Ca;
			Cur.second=Cur.first+Cur.second-Ca;
		}
		else{
			Cur.second=0;
			Cur.first=Cur.first+Cur.second;
		}
		Push(Cur);
	}
	cout<<"can't arrive at there!";
}


int main(){
	cin>>Ca>>Cb>>Ct;
	BFS({0,0});
	return 0;
}

我简述一下这个题的题意,有两个水桶A、B,初始状态都没有水。接下来我们有以下改变状态的操作

A或B水装满
A或B水倒空
A向B倒水,如果水满停止倒水
B向A倒水,如果水满停止倒水

如果能找到A+B的水刚好为Ct,输出TRUE
否则输出can’t arrive at there!

输入三个参数:Ca,Cb,Ct,分别为A,B,桶的容量和目标容量
分析题意:
我们发现这是一个典型的从一个状态可以转换到另外几个状态,来寻找状态,那我们就会想到搜索,我们把所有可能的结果都搜索一遍,以上代码就是使用了广度优先搜索。

好有没有人提出疑问?为什么一定要用先进先出的队列呢?我用后进先出的栈可以吗?

完全可以。但是它此时拥有了一个新的名字深度优先搜索。

到现在我们应该也理解了从一个状态可以到达几个状态,即一对多的处理思路吧。当我们将队列换栈的时候,我们会发现我们去下个状态点的时候永远取的是当前栈的最后一个点。这样就达到一种搜索,每一次都是从第一层搜索到最后一层,保证路径无重复,所以它优先的是深度。

因此我们可以总结:
用队列实现1对多的状态转移实现的是以广度优先的搜索
用栈实现1对多的状态转移实现的是以深度优先的搜索

注:在实现深度优先搜索时,个人建议使用系统栈。读者可自行考虑系统栈实现相对于自定义栈的优势。

2.抽象搜索
在我们理解了一对多的搜索后。结合之前一对一的搜索遍历,我们可以再抽取出搜索的一个特性:状态转移。事实上,搜索的核心理解就是对状态转移的理解。

无论一对一还是一对多,我们搜索的基础元素都是个体,都是一个点。

那我们可不可以从一个以一个集合为基础元素呢?

当然可以。

给大家讲一个简单的例子:二分搜索

这里我们就把数组这个集合作为一个整体。

搜索这个数组里面有没有某一个元素。可以转变为两个状态,这个数组的左半边是否有这个元素,这个数组右半边是否有着个元素。然后依次分解,直到找到这个元素,或者全部找完都没有。

类似的,我们可以汲取这种将大集合问题转换成多个小集合问题。这就是我常用的一种思想分治。

基于分治我们出现了很多优化算法,比如二分查找、二分递归、快速排序等等。

3.搜索总结:搜索的核心有结果完备性、无重复性、状态转移。

学到了这里我希望朋友们能抽取一个方式的核心要素进行自行推动。
同时不要局限于个体之间关联,可以试着考虑集合与个体,集合与集合,甚至集合与群等等之间的关系。

这里我给朋友们留下的思考空间很大,比如广度优先搜索的状态转移代价不一样怎么办?你可以去看看迪利斯特拉和SPFA算法等等。

记住这个世界上的绝大多数事是可以通过搜索到达的,状态转移时你需要考虑的核心与关键,下一篇我将以搜索的状态转移为核心,讲解动态规划,加油。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值