【c++算法篇】--30分钟从0到精通讲解算法--搜索

c++搜索算法---详解

  • 搜索算法概念
  • 具体实现
    • 深度优先搜索(dfs)
      • 原理/思路
      • 实现过程
        • 例题
          • 题目1
          • 题目2
          • 题目3
          • 题目4
        • AC代码+思路详解
          • 题目1
          • 题目2
          • 题目3
          • 题目4
    • 广度优先搜索(bfs)
      • 原理/思路
      • 实现过程
        • 例题
          • 题目1
          • 题目2
          • 题目3
        • AC代码+思路详解
          • 题目1
          • 题目2
          • 题目1
    • 总结:
      • 答疑解惑
          • 方向数组

搜索是c语言中重要的算法之一,为以后的学习打下了基础,下面让我们来了解一下:
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第1张图片

搜索算法概念

是通过进行递归操作或者通过队列的特殊性质来进行解题的一种算法,具体分为深度优先搜索(dfs)和广度优先搜索(bfs),在具体解决题目的过程中可以通过题目来选择不同的解法,在数据范围允许的情况下,两种方式可以进行互相转换,下面会进行具体介绍。

具体实现

深度优先搜索(dfs)

是以深度为优先的搜索方式,在一些数据范围不大的走迷宫类型的题目中可以运用,通过不停的试错的方式找寻答案,但是有可能会mle

原理/思路

如下
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第2张图片
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第3张图片
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第4张图片
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第5张图片
后面以此类推
所以深度搜索就是依靠递归操作寻找答案。
注意:
题目如果数据范围过大不建议使用,很有可能可以找到答案,但是需要大量时间(实在没有办法就打表吧。

实现过程

先来看到题
<<
放苹果:
描述
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
输入
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
输出
对输入的每组数据M和N,用一行输出相应的K。
样例输入
1
7 3
样例输出
8
>>
有没有感到熟悉
直接上代码

#include
#include
#include
#include
using namespace std;
int vis,m,n;
void dfs(int l,int p,int q){//三个值的含义主函数内有介绍
	if(q==n){//在每一次递归开始的时候判断一下是否已经完成题目要求,是的话直接记录答案结束就好(下面有递归函数所以不能放在下面也不能放在循环里)
		if(p==0){//当q==n说明盘子全部放满,题目还要求苹果全部放完,所以需要特殊判断
			vis++;
		}	
		return;
	}
	for(int i=l;i<=p;i++){//循环剩余的苹果
		dfs(i,p-i,q+1);//自己理解体会一下
	}
}
int main(){//主函数部分按照题目要求写就好,终点是dfs里的内容
	int t;
	cin>>t;
	while(t!=0){//多组数据
		vis=0;
		t--;
		cin>>m>>n;
		dfs(0,m,0);//注意我们开始递归的值
		/*
		注意,我们开始的第一个0代表处理了几个苹果,第二个代表剩余苹果,第三个0代表处理到第几个盘子*/
		cout<<vis<<endl;
		
	}
}

现在是不是对深度优先搜索有了一个初步认识了,下面来总结一下

void dfs(按照题目要求传入参数){
	if(判断结束条件){
	****
	return ;
	}
	for(继续处理){
	dfs(进行递归);
	}
}

【c++算法篇】--30分钟从0到精通讲解算法--搜索_第6张图片

模板总结完毕,下面来实操一下

例题
题目1

题目如下:
8465:马走日
总时间限制: 1000ms 内存限制: 65536kB
描述
马在中国象棋以日字形规则移动。
请编写一段程序,给定n*m大小的棋盘,以及马的初始位置(x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。
输入
第一行为整数T(T < 10),表示测试数据组数。
每一组测试数据包含一行,为四个整数,分别为棋盘的大小以及初始位置坐标n,m,x,y。(0<=x<=n-1,0<=y<=m-1, m < 10, n < 10)
输出
每组测试数据包含一行,为一个整数,表示马能遍历棋盘的途径总数,0为无法遍历一次。
样例输入
1
5 4 0 0
样例输出
32
提示:可以从棋盘大小,马下一步的移动位置思考

题目2

题目如下:
1789.算24
描述
给出4个小于10个正整数,你可以使用加减乘除4种运算以及括号把这4个数连接起来得到一个表达式。现在的问题是,是否存在一种方式使得得到的表达式的结果等于24。
这里加减乘除以及括号的运算结果和运算的优先级跟我们平常的定义一致(这里的除法定义是实数除法)。
比如,对于5,5,5,1,我们知道5 * (5 – 1 / 5) = 24,因此可以得到24。又比如,对于1,1,4,2,我们怎么都不能得到24。
输入
输入数据包括多行,每行给出一组测试数据,包括4个小于10个正整数。最后一组测试数据中包括4个0,表示输入的结束,这组数据不用处理。
输出
对于每一组测试数据,输出一行,如果可以得到24,输出“YES”;否则,输出“NO”。
样例输入
5 5 5 1
1 1 4 2
0 0 0 0
样例输出
YES
NO
提示:可以将所有的操作都递归进去

题目3

题目如下:
1818.红与黑
描述
有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。
输入
包括多个数据集合。每个数据集合的第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都不超过20。在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下
1)‘.’:黑色的瓷砖;
2)‘#’:红色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。
当在一行中读入的是两个零时,表示输入结束。
输出
对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。
样例输入
6 9
…#.
…#





#@…#
.#…#.
0 0
样例输出
45
提示:普通的走迷宫,稍加判断位置即可

题目4

题目如下:
1805:碎纸机
描述
你现在负责设计一种新式的碎纸机。一般的碎纸机会把纸切成小片,变得难以阅读。而你设计的新式的碎纸机有以下的特点:
1.每次切割之前,先要给定碎纸机一个目标数,而且在每张被送入碎纸机的纸片上也需要包含一个数。
2.碎纸机切出的每个纸片上都包括一个数。
3.要求切出的每个纸片上的数的和要不大于目标数而且与目标数最接近。
举一个例子,如下图,假设目标数是50,输入纸片上的数是12346。碎纸机会把纸片切成4块,分别包含1,2,34和6。这样这些数的和是43 (= 1 + 2 + 34 + 6),这是所有的分割方式中,不超过50,而又最接近50的分割方式。又比如,分割成1,23,4和6是不正确的,因为这样的总和是34 (= 1 + 23 + 4 + 6),比刚才得到的结果43小。分割成12,34和6也是不正确的,因为这时的总和是52 (= 12 + 34 + 6),超过了50。
还有三个特别的规则:
1.如果目标数和输入纸片上的数相同,那么纸片不进行切割。
2.如果不论怎样切割,分割得到的纸片上数的和都大于目标数,那么打印机显示错误信息。
3.如果有多种不同的切割方式可以得到相同的最优结果。那么打印机显示拒绝服务信息。比如,如果目标数是15,输入纸片上的数是111,那么有两种不同的方式可以得到最优解,分别是切割成1和11或者切割成11和1,在这种情况下,打印机会显示拒绝服务信息。
为了设计这样的一个碎纸机,你需要先写一个简单的程序模拟这个打印机的工作。给定两个数,第一个是目标数,第二个是输入纸片上的数,你需要给出碎纸机对纸片的分割方式。
输入
输入包括多组数据,每一组包括一行。每行上包括两个正整数,分别表示目标数和输入纸片上的数。已知输入保证:两个数都不会以0开头,而且两个数至多都只包含6个数字。
输入的最后一行包括两个0,这行表示输入的结束。
输出
对每一组输入数据,输出相应的输出。有三种不同的输出结果:
sum part1 part2 …
rejected
error
第一种结果表示:
1.每一个partj是切割得到的纸片上的一个数。partj的顺序和输入纸片上原始数中数字出现的次序一致。
2.sum是切割得到的纸片上的数的和,也就是说:sum = part1 + part2 +…
第一种结果中相邻的两个数之间用一个空格隔开。
如果不论怎样切割,分割得到的纸片上数的和都大于目标数,那么打印“error”。
如果有多种不同的切割方式可以得到相同的最优结果,那么打印“rejected”。
样例输入
50 12346
376 144139
927438 927438
18 3312
9 3142
25 1299
111 33333
103 862150
6 1104
0 0
样例输出
43 1 2 34 6
283 144 139
927438 927438
18 3 3 12
error
21 1 2 9 9
rejected
103 86 2 15 0
rejected
提示:读题

AC代码+思路详解
题目1

题解如下:

#include
#include
#include
#include
using namespace std ;
char s[105][105];
int ans,n,m,cnt;
int dx[9]={0,-2,-2,-1,-1,2,2,1,1};
int dy[9]={0,-1,1,-2,2,1,-1,2,-2};//方向数组,按照题目要求写即可
//如有不理解可以在目录下方寻找有讲解

void dfs(int x,int y,int step){
	s[x][y]='#';//去重
	if(!step) 
	ans++;
	for(int i=1;i<=8;i++){
		int tx=x+dx[i];
		int ty=y+dy[i];	
		if(s[tx][ty]=='.') dfs(tx,ty,step-1);//走下一步并且总点数减1
	}
	s[x][y]='.';
}

int main()
{
	int T;cin >> T;
	while(T--){
		memset(s,'#',sizeof(s));
		int x,y;ans=0;
		cin >> n >> m >> x >> y;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				s[i][j]='.';
		x++;y++;dfs(x,y,n*m-1);//这里是从原点开始递归,n*m代表需要走的总点数(因为需要遍历棋盘),n*m-1是因为需要排除原点(走过了)
		printf("%d\n",ans);
	}
	return 0;
} 
题目2

题解如下:

#include
#include
#include
#include
using namespace std;
double a[8]={1,1,1,1},b[10087];
int f=0;
void dfs(int n){
	if(n==4){
		if(a[1]>23.9999&&a[1]<24.000001)//注意数据类型
			f++;
			return;
	}
	if(f==0){
		for(int i=1;i<=4;i++){
			if(b[i]==1) continue;
			for(int j=i+1;j<=4;j++){
				if(b[j]==1) continue;
				b[j]=1;
				double x=a[i],y=a[j];//递归所有操作
				a[i]=x+y;dfs(n+1);
				a[i]=x/y;dfs(n+1);
				a[i]=x-y;dfs(n+1);
				a[i]=x*y;dfs(n+1);
				a[i]=y-x;dfs(n+1);
				a[i]=y/x;dfs(n+1);
				b[j]=0;
				a[i]=x;a[j]=y;
			}
		}
	}
}
int main(){
	while(a[1]+a[2]+a[3]+a[4]!=0){
		for(int i=1;i<=4;i++){
			cin>>a[i];
		}
		if(a[1]+a[2]+a[3]+a[4]==0){
			break;
		}
		memset(b,0,sizeof(b));//考虑到多组数据,需要将b清零
		dfs(1);
		if(f!=0) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
		f=0;
	}
}
题目3

题解如下:

#include
#include
#include
#include
using namespace std;
char mapp[1011][1011];
int mpp[1011][1011];
int vis,x,w,h,y;
int dx[5]={
	0,1,-1,0,0//本人喜欢循环1开始,所以下标前多加个0
};
int dy[5]={
	0,0,0,1,-1
};
void dfs(int a,int b){
	for(int i=1;i<=4;i++){
			int tx=a+dx[i];
			int ty=b+dy[i];//利用方向数组
			mpp[a][b]=1;
			if(tx<=0||ty<=0||tx>w||ty>h||mpp[tx][ty]==1||mapp[tx][ty]=='#'){//判断边界条件
				continue;		
			}
			mpp[tx][ty]=1;
			vis++;
			dfs(tx,ty);
		}
}
int main(){
	while(1){
		memset(mpp,0,sizeof(mpp));
		vis=0;
		cin>>h>>w;
		if(w==0&&h==0){
			return 0;
		}
		for(int i=1;i<=w;i++){
			for(int j=1;j<=h;j++){
				cin>>mapp[i][j];
				if(mapp[i][j]=='@'){//寻找起点
					x=i,y=j;
				}
			}
		}
		dfs(x,y);
		cout<<vis+1<<endl;//原点不要忘

	}
}


题目4

题解如下:

#include
#include
#include
#include
#include
using namespace std;
int v;
int cnt;
int n;
int s;
char a[404];
int maxx=0;
int a1[404],a2[404];
void dfs(int x,int y,int z){
	if(y>n){
		return ;
	}
	if(x>=s){//注意题目要求即可
		if(y==maxx){
			v++;
		}
		if(y>maxx){
			maxx=y;
			v=1;
			for(int i=1;i<z;i++){
				a2[i]=a1[i];
			}
			cnt=z-1;
			return;
		}
	}
	int tmp=0;
	for(int i=x;i<s;i++){
		tmp=tmp*10+a[i]-'0';
		a1[z]=tmp;
		dfs(i+1,y+tmp,z+1);
	}
}
int main(){
	while(1){
		v=0;
		maxx=0;
		cin>>n>>a;
		s=strlen(a);
		if(n==0) break;
		if(atoi(a)==n){
			cout<<n<<" "<<n<<endl;
			continue;
		}
		dfs(0,0,1);
		if(maxx==0){//按照题目要求输出
			cout<<"error"<<endl;
			continue;
		}
		if(v!=1){
			cout<<"rejected"<<endl;
			continue;
		}
		cout<<maxx<<" ";
		for(int i=1;i<=cnt;i++){
			cout<<a2[i]<<" ";
		}
		cout<<endl;

	}
}

广度优先搜索(bfs)

先普及一下队列概念
入队
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第7张图片
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第8张图片
出队
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第9张图片
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第10张图片

由此可见:队列的是先进先出

原理/思路

如下:
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第11张图片
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第12张图片
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第13张图片
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第14张图片

实现过程

老规矩看到题
<<
2753:走迷宫
总时间限制: 1000ms 内存限制: 65536kB
描述
一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。
给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜着走。
输入
第一行是两个整数,R和C,代表迷宫的长和宽。( 1<= R,C <= 40)
接下来是R行,每行C个字符,代表整个迷宫。
空地格子用’.‘表示,有障碍物的格子用’#‘表示。
迷宫左上角和右下角都是’.'。
输出
输出从左上角走到右下角至少要经过多少步(即至少要经过多少个空地格子)。计算步数要包括起点和终点。
样例输入
5 5
…###
#…
#.#.#
#.#.#
#.#…
样例输出
9
>>
上代码

#include
#include
#include
#include
#include
using namespace std;
int m,n,bs=0;
struct aaa{
	int x,y;//记录位置
};
int vis[404][404];
int dis[404][404];
int py[404][404];
char map[404][404];
int dx[5]={0,1,-1,0,0};
int dy[5]={0,0,0,1,-1};
void bfs(int x,int y){
	memset(vis,0,sizeof(vis));
	memset(dis,0x7f7f7f7f,sizeof(dis));//初始化数据
	vis[x][y]=1;
	dis[x][y]=0;
	queue <aaa> q;//定义一个aaa类型的队列,让队列有aaa结构体的特性
	q.push((aaa){x,y});//入队
	while(!q.empty()){//这句话意思是当队列不为空时
		aaa a;
		a=q.front();//取出队首元素
		q.pop();//出队
		for(int i=1;i<=4;i++){//广搜
			int tx=a.x+dx[i];
			int ty=a.y+dy[i];
			if(tx<1||tx>m||ty<1||ty>n||py[tx][ty]==1||vis[tx][ty]==1||dis[tx][ty]<dis[a.x][a.y]+1) continue;
			q.push((aaa){tx,ty});//入队所有符合题意情况
			dis[tx][ty]=dis[a.x][a.y]+1;
			vis[tx][ty]=1;
			if(tx==m&&ty==n) return;

		}

	}
	return ;

}
int main(){
	scanf("%d %d",&m,&n);
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			scanf(" %c",&map[i][j]);
			if(map[i][j]=='#'){
				py[i][j]=1;
			}else{
				py[j][j]=0;
			}
		}
	}
	bfs(1,1);
	cout<<dis[m][n]+1;

}

现在是不是对深度优先搜索有了一个初步认识了,下面来总结一下

void bfs(){
	push//入队
	while(empty){
		front//取出队首元素
		pop出队
		for(循环处理){
			if(判断不符合题意条件) continue;
			push//入队新元素
			if(判断结束条件){
			}
		}
	}
}

【c++算法篇】--30分钟从0到精通讲解算法--搜索_第15张图片

例题
题目1

题目如下:
2727:仙岛求药
总时间限制: 1000ms 内存限制: 65536kB
描述
少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶。叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处。迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全。现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他。现在要求你来帮助他实现这个目标。
下图 显示了一个迷阵的样例及李逍遥找到仙药的路线.
输入
输入有多组测试数据. 每组测试数据以两个非零整数 M 和 N 开始,两者均不大于20。M 表示迷阵行数, N 表示迷阵列数。接下来有 M 行, 每行包含N个字符,不同字符分别代表不同含义:

  1. ‘@’:少年李逍遥所在的位置;
  2. ‘.’:可以安全通行的方格;
  3. ‘#’:有怪物的方格;
  4. ’:仙药所在位置。
    当在一行中读入的是两个零时,表示输入结束。
    输出
    对于每组测试数据,分别输出一行,该行包含李逍遥找到仙药需要穿过的最少的方格数目(计数包括初始位置的方块)。如果他不可能找到仙药, 则输出 -1。
    样例输入
    8 8
    .@##…#
    #…#.#
    #.#.##…
    …#.###.
    #.#…#.
    …###.#.
    …#.

    .#…###
    6 5
    ..#.
    .#…
    …##.

    .#…
    …@
    9 6
    .#…#.
    .#.
    .#
    .####.
    …#…
    …#…
    …#…
    …#…
    #.@.##
    .#…#.
    0 0
    样例输出
    10
    8
    -1
    提示:见例题
题目2

题目如下:
7218:献给阿尔吉侬的花束
总时间限制: 100ms 内存限制: 65536kB
描述
阿尔吉侬是一只聪明又慵懒的小白鼠,它最擅长的就是走各种各样的迷宫。今天它要挑战一个非常大的迷宫,研究员们为了鼓励阿尔吉侬尽快到达终点,就在终点放了一块阿尔吉侬最喜欢的奶酪。现在研究员们想知道,如果阿尔吉侬足够聪明,它最少需要多少时间就能吃到奶酪。
迷宫用一个R×C的字符矩阵来表示。字符S表示阿尔吉侬所在的位置,字符E表示奶酪所在的位置,字符#表示墙壁,字符.表示可以通行。阿尔吉侬在1个单位时间内可以从当前的位置走到它上下左右四个方向上的任意一个位置,但不能走出地图边界。
输入
第一行是一个正整数T(1 <= T <= 10),表示一共有T组数据。
每一组数据的第一行包含了两个用空格分开的正整数R和C(2 <= R, C <= 200),表示地图是一个R×C的矩阵。
接下来的R行描述了地图的具体内容,每一行包含了C个字符。字符含义如题目描述中所述。保证有且仅有一个S和E。
输出
对于每一组数据,输出阿尔吉侬吃到奶酪的最少单位时间。若阿尔吉侬无法吃到奶酪,则输出“oop!”(只输出引号里面的内容,不输出引号)。每组数据的输出结果占一行。
样例输入
3
3 4
.S…
###.
…E.
3 4
.S…
.E…

3 4
.S…
.####
…E.
样例输出
5
1
oop!

题目3

题目如下:
6044:鸣人和佐助
查看提交统计提问
总时间限制: 1000ms 内存限制: 65536kB
描述
佐助被大蛇丸诱骗走了,鸣人在多少时间内能追上他呢?
已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置。地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下,需要先打败大蛇丸的手下才能到这些位置。鸣人有一定数量的查克拉,每一个单位的查克拉可以打败一个大蛇丸的手下。假设鸣人可以往上下左右四个方向移动,每移动一个距离需要花费1个单位时间,打败大蛇丸的手下不需要时间。如果鸣人查克拉消耗完了,则只可以走到没有大蛇丸手下的位置,不可以再移动到有大蛇丸手下的位置。佐助在此期间不移动,大蛇丸的手下也不移动。请问,鸣人要追上佐助最少需要花费多少时间?
输入
输入的第一行包含三个整数:M,N,T。代表M行N列的地图和鸣人初始的查克拉数量T。0 < M,N < 200,0 ≤ T < 10
后面是M行N列的地图,其中@代表鸣人,+代表佐助。*代表通路,#代表大蛇丸的手下。
输出
输出包含一个整数R,代表鸣人追上佐助最少需要花费的时间。如果鸣人无法追上佐助,则输出-1。
样例输入
样例输入1
4 4 1
#@##
**##
###+
样例输入2
4 4 2
#@##
**##
样例输出
样例输出1
6
样例输出2
4

AC代码+思路详解
题目1

题解如下:
典型走迷宫,自己领悟一下;

#include
#include
#include
#include
#include
using namespace std;
struct abc{
	int x,y;
};
int dx[5]={
	0,0,0,1,-1
};
int dy[5]={
	0,1,-1,0,0
};
int n=1,m=1,xx,yy;
int vis[404][404];
char mp[404][404];
void bfs(int x,int y){
	memset(vis,0x7f7f7f7f,sizeof(vis));
	vis[x][y]=1;
	queue<abc>p;
	p.push((abc){x,y});
	while(!p.empty()){
		abc a=p.front();

		for(int i=1;i<=4;i++){
			int tx=a.x+dx[i];
			int ty=a.y+dy[i];
			if(tx<1||ty<1||tx>m||ty>n||mp[tx][ty]=='#'||vis[a.x][a.y]+1>vis[tx][ty]){
				continue;
			}
			p.push((abc){
					tx,ty
					});


			mp[a.x][a.y]='#';
			vis[tx][ty]=vis[a.x][a.y]+1;
			if(tx==xx&&ty==yy){
				return ;
			}

		}
		p.pop();
	}
}
int main(){
	int x,y;
	while(n+m!=0){
		cin>>m>>n;
		if(m+n==0) continue;
		for(int i=1;i<=m;i++){
			for(int j=1;j<=n;j++){
				scanf(" %c",&mp[i][j]);
				if(mp[i][j]=='@'){
					x=i,y=j;	
				}else if(mp[i][j]=='*'){
					xx=i,yy=j;
				}
			}
		}
		bfs(x,y);
		if(vis[xx][yy]==0x7f7f7f7f) cout<<-1<<endl;
		else cout<<vis[xx][yy]-1<<endl;
		memset(mp,0,sizeof(mp));
	}
}
题目2

题解如下:

#include
#include
#include
#include
#include
using namespace std;
int mapp[404][404],n,m,tx,ty;
int dis[404][404];
int xx[500]={0,0,0,1,-1};
int yy[500]={0,1,-1,0,0};
struct aaa{
	int x,y;
};
void bfs(int x,int y){
	memset(dis,0x7f7f7f7f,sizeof(dis));
	dis[x][y]=0;
	queue<aaa>p;
	p.push((aaa){
			x,y
			});
	while(!p.empty()){
		aaa pp;
		pp=p.front();
		for(int i=1;i<5;i++){
			int xxx=pp.x+xx[i];
			int yyy=pp.y+yy[i];
			if(xxx<1||xxx>n||yyy<1||yyy>m||mapp[xxx][yyy]==1||dis[pp.x][pp.y]+1>dis[xxx][yyy]) continue;
			mapp[xxx][yyy]=1;
			dis[xxx][yyy]=dis[pp.x][pp.y]+1;
			p.push((aaa){xxx,yyy});
			if(xxx==tx&&yyy==ty) return;//此题规定了起点终点
		}
		p.pop();
	}
	return ;
}
int main(){
	int k;
	cin>>k;
	for(int z=1;z<=k;z++){
		scanf("%d %d",&n,&m);
		char s[404][404];
		int x,y;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
			scanf(" %c",&s[i][j]);
				if(s[i][j]=='#'){
					mapp[i][j]=1;
				}else if(s[i][j]=='.'){
					mapp[i][j]=0;
				}else if(s[i][j]=='S'){
					x=i,y=j;
				}else{
					tx=i,ty=j;
				}
			}
		}
		bfs(x,y);
		if(dis[tx][ty]==0x7f7f7f7f) cout<<"oop!"<<endl;
		else cout<<dis[tx][ty]<<endl;
		memset(mapp,0,sizeof(mapp));
	}
}
题目1

题解如下:

#include
#include
#include
#include
#include
using namespace std;
int m,n,xx,yy,op;
struct abc{
	int x,y,oo;
};
int  r[201][201][25],vis[201][201][25];

char mpp[201][201];
int dx[5]={
	0,0,0,1,-1
};
int dy[5]={
	0,1,-1,0,0
};
void bfs(int x,int y){
	memset(vis,0,sizeof(vis));
	memset(r,0x7f7f7f7f,sizeof(r));
	vis[x][y][op]=1;
	r[x][y][op]=0;
	queue<abc>p;
	p.push((abc){x,y,op});
	while(!p.empty()){
		abc a=p.front();
		for(int i=1;i<=4;i++){
			int tx=a.x+dx[i];
			int ty=a.y+dy[i];
			int tt=a.oo;
			if(tx>m||ty>n||tx<1||ty<1||vis[tx][ty][tt]==1) continue;
			if(mpp[tx][ty]=='#'){
				if(tt) tt--;
				else continue;
			}
			r[tx][ty][tt]=r[a.x][a.y][a.oo]+1;

			if(tx==xx&&ty==yy){
				cout<<r[xx][yy][tt];
				return ;
			}
			vis[tx][ty][tt]=1;
			p.push((abc){tx,ty,tt});

		}
		p.pop();
	}
	cout<<-1;

}
int main(){
	int x,y;
	cin>>m>>n>>op;
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++){
			scanf(" %c",&mpp[i][j]);
			if(mpp[i][j]=='@') x=i,y=j;	
			else if(mpp[i][j]=='+') xx=i,yy=j;	
		}
	bfs(x,y);


}

总结:

通过以上题目和介绍,想必各位对搜索有了基本的认识,在使用过程中也要注意两种方式的互相转换,对于搜索题,大部分都是套模板,但是各位还需要清楚了解搜索的原理和实现过程,今天的介绍到此结束,谢谢大家。
都看到这里了,三联关注一下?嘻嘻^ ^

答疑解惑

方向数组

具体思路如下:
【c++算法篇】--30分钟从0到精通讲解算法--搜索_第16张图片

你可能感兴趣的:(c++刷题篇,算法,c++,深度优先)