深搜
过程:
对每一个可能的分支路径深入到不能再深入为止。而且每个节点只能访问一次
【第一题:孙悟空找师傅】
问题描述:西游路上咱们的唐长老又一次被妖怪抓走了。已知妖怪洞穴是一个N*N 的正方形,其中有一些假山堵路。
输入:第一行是一个正整数N(2<N<10),后面包含N*N行由0,1,2组成的矩阵,其中0表示可以走,1表示假山,2表示师傅的位置。
输出:如果悟空和八戒可以救出师傅就输出YES,否则就输出NO。
样例输入:
4
0 0 0 0
0 1 0 1
1 0 0 1
1 1 0 2
样例输出:
YES
思路:递归的思想,在二维的数组里面遍历上下左右四个方向
代码示例:
#include<iostream>
using namespace std;
/*
4
0 0 0 0
0 1 0 1
1 0 0 1
1 1 0 2
*/
//深搜,孙悟空在地图的左上角,师傅所在的地方为2,
//有石头的地方为1,有路的地方为0,,请输入一个n,以及地图
//寻找孙悟空是否能够找到师傅,可以输出YES,否则输出NO
int dx[5]={-1,0,1,0},dy[5]={0,1,0,-1};//控制搜索方向
//确保四个方向能够搜到
int G[105][105],vis[105][105]; //G存储地图,
//vis标记是否搜索过
int n;//n为地图的长和宽
bool f=false;//标记是否能够找到师傅
void dfs(int x,int y){//如果找到师傅则结束
if(G[x][y]==2){
cout<<x<<" "<<y<<endl;
f=true;
return;
}
//开始搜索
for(int i=0;i<4;i++){ //四个方向
int xx=x+dx[i];
int yy=y+dy[i];
if(xx>=0&&xx<n&&yy>=0&&yy<n&&G[xx][yy]!=1&&vis[xx][yy]==0){
vis[xx][yy]=1;//标记xx yy位置已经搜索过了
dfs(xx,yy);//从xx yy位置开始搜索
vis[xx][yy]=0;//回溯
}
}
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>G[i][j];
}
}
if(G[0][0]==1){
cout<<"NO"<<endl;
return 0;
}
else if(G[0][0]==2){
cout<<"YES"<<endl;
return 0;
}
else{
vis[0][0]=1;//标记0 0 位置搜索过了
dfs(0,0);
if(f){
cout<<"YES"<<endl;
}
else{
cout<<"NO"<<endl;
}
}
return 0;
}
【第二题:传送门】
问题描述:
观音有一件法宝,叫做传送门,我们可以通过这件法宝从妖怪洞穴出去,了保密,观音把这件法宝放在了一个妖怪洞穴,我们只要找到那个洞穴就可以出去了。观音已经把放法宝洞穴的坐标告诉我了,我们需要编程确定一下是否能到达那个洞穴就行了。唐老大:空空,听紧箍咒和编程你选一个吧。空空:你.(吐血三升),你把样例先告诉我。
输入:第一行包含一个正整数N(1<n<30)表示迷宫的长和宽,下面N行,每行有N个数字,其中1表示可以走,0表示死路。接下来两行分别表示悟空和师傅所在洞穴坐标和传送门所在洞穴坐标。
输入:如果悟空他们可以成功走到传送门空间就输出YES,否则就输出 No。
样例输入:
5
1 0 0 1 0
1 1 1 1 1
1 0 1 0 0
1 0 1 1 1
0 1 0 1 0
4 4
2 1
样例输出:
YES
代码示例:
#include<iostream>
using namespace std;
//输入一个地图,1为可走,0为不可走,下面输入孙悟空所在的坐标
//以及传送门所在的坐标,深度优先搜索看孙悟空是否能找到传送门
/*
输入:1
5
1 0 0 1 0
1 1 1 1 1
1 0 1 0 0
1 0 1 1 1
0 1 0 1 0
4 4
2 1
输出:
YES
*/
int dx[5]={-1,0,1,0},dy[5]={0,1,0,-1};//遍历方向
int n;//存储地图
int G[1001][1001],vis[1001][1001];//G存储地图,vis标记是否被搜索过
bool f=false;//存储是否找到传送门
int sx,sy,cx,cy;
void dfs(int x,int y){
if(x==cx&&y==cy){
cout<<"YES"<<endl;
f=true;
return ;
}
for(int i=0;i<4;i++){
int xx=x+dx[i];
int yy=y+dy[i];
if(xx>=1&&xx<=n&&yy>=1&&yy<=n&&G[xx][yy]!=0&&vis[xx][yy]==0){
vis[xx][yy]=1;
dfs(xx,yy);
vis[xx][yy]=0;
}
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>G[i][j];
}
}
cin>>sx>>sy;
cin>>cx>>cy;
if(sx==cx&&cy==sy){
cout<<"YES"<<endl;
return 0;
}
vis[sx][sy]=1;
dfs(sx,sy);
if(f==false){
cout<<"NO"<<endl;
}
return 0;
}
【题目三:西游记之海域分块】
问题描述:
最近天气炎热,人鱼王国水位下降陆地露出海面,把王国分成了几个部分,国王的命令无法传达给公民,让红烧鱼国王感觉非常苦恼,所以国王想要请悟空帮忙给各个水域传递一个消息。虽然人鱼王国不大,但是如果悟空每个地方都去一次就会耽误很长时间,影响取经大业,所以悟空决定先计算一下陆地把人鱼王国分成了几部分。输入:第一行包合两个正整数N和M(1<N,M<30)表示人鱼王国的长和宽,下面是一个N行M列的二维数组,其中1表示陆地,0表示海水。输入:一个整数a,表示陆地把海域分成的份数(斜着方向不算连通)。
样例输入:
5 5
1 0 0 1 0
0 1 0 0 1
1 0 1 0 0
1 0 0 1 1
0 1 0 1 0
样例输出:
6
核心思路:
从第一行开始从左向右开始遍历,如果当前位置是0,就从当前位置开始向四周深搜,把相连的0都变成1
代码示例:
#include<iostream>
using namespace std;
/*
输入:第一行包含两个正整数N和M表示人鱼王国的长和宽 ,下面是一个
N行M列的二维数组,其中1表示陆地,0表示海水
输出:一个整数a表示陆地把海域分成的份数(斜着方向不算通)
样例输入:
5 5
1 0 0 1 0
0 1 0 0 1
1 0 1 0 0
1 0 0 1 1
0 1 0 1 0
样例输出:
6
*/
//核心思路:从第一行开始,从左向右开始遍历,如果当前位置是0,
//就从当前位置开始向四周深搜,把相连的 0都变成 1
int n,m;
int G[1001][1001];
int dx[5]={1,0,-1,0},dy[5]={0,1,0,-1};
void dfs(int x,int y){
for(int i=0;i<4;i++){
int xx=x+dx[i];
int yy=y+dy[i];
if(xx>=0&&xx<n&&yy>=0&&yy<m&&G[xx][yy]==0){
G[xx][yy]=1;
dfs(xx,yy);
}
}
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>G[i][j];
}
}
int res=0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(G[i][j]==0){
G[i][j]=1;
dfs(i,j);
res++;
}
}
}
cout<<res<<endl;
return 0;
}
【题目四: 国王的神器 】
问题描述:
红烧鱼国王拿出了祖传神器(水管),他可以使斜对角的海域连通,这样悟空就可以少通知一些地方了,真是个不错东西,我们-起来计算一下使用神器后,悟空需要通知几个海域。
输入:第一行包含两个正整数N和M(1<N,M<30)表示人鱼王国的长和宽,下面是
一个N行M列的二维数组,其中1表示陆地,0表示海水。
输入:一个整数a表示陆地把海域分成的份数(斜着方向连通)。
样例输入:
5 5
1 0 0 1 0
0 1 0 0 1
1 0 1 0 0
1 0 0 1 1
0 1 0 1 0
样例输出:
2
代码示例:
#include<iostream>
using namespace std;
/*
5 5
1 0 0 1 0
0 1 0 0 1
1 0 1 0 0
1 0 0 1 1
0 1 0 1 0
*/
int G[1001][1001];
int n,m,mins=2147483647,sum=0;
int dx[5]={0,1,0,-1},dy[5]={1,0,-1,0};
void dfs(int x,int y){
for(int dx=-1;dx<=1;dx++){
for(int dy=-1;dy<=1;dy++){
int xx=x+dx;
int yy=y+dy;
if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&G[xx][yy]==0){
G[xx][yy]=1;
dfs(xx,yy);
}
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>G[i][j];
}
}
int res=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(G[i][j]==0){
if(G[i][j]==0){
G[i][j]=1;
dfs(i,j);
res++;
}
}
}
}
cout<<res<<endl;
return 0;
}
【题目五:唐僧去哪儿?】
问题描述:
历经磨难,悟空和三藏终于逃出魔爪,正准备去找沙僧等人,问题又来了。他们的位置离沙僧比较远,现在有一副地图。 地图每个位置都有一个数字表示经过这个位置需要用的时间(本来就一个筋斗云的事,带着师傅就是麻烦)地图如下:
1 6 6 6
15 7 6 6
15 3 6 6
15 15 1 1
输入:第一行包含两个正整数N和M(1N, M<30)表示人鱼王国的长和宽下面一行包含4个整数,前两个数表示猴哥和师傅的位置,后面两个数表示沙僧位置,最后是一个N行M列的二维数组,每个数字表示走当前位置需要用的时间。
输出:一个整数,表示最短时间。
【样例输入】
4 4
1 1 4 4
1 6 6 6
15 7 6 6
15 3 6 6
15 15 1 1
【样例输出】
25
代码示例:
【题目六:红与黑】
有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。
你站在其中一块黑色的瓷砖上,只能向相邻(上下左右四个方向)的黑色瓷砖移动。
请写一个程序,计算你总共能够到达多少块黑色的瓷砖。
输入格式
输入包括多个数据集合。
每个数据集合的第一行是两个整数 W和 H,分别表示 x 方向和 y方向瓷砖的数量。
在接下来的 H行中,每行包括 W 个字符。每个字符表示一块瓷砖的颜色,规则如下
1)‘.’:黑色的瓷砖;
2)‘#’:红色的瓷砖;
3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。
当在一行中读入的是两个零时,表示输入结束。
输出格式
对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。
数据范围
1≤W,H≤20
输入样例:
6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
0 0
输出样例:
45
代码示例:
示例1:(两种方法差不多,哪个好理解写哪个!!!都是用DFS写的)
#include<bits/stdc++.h>
using namespace std;
const int N=21;
char g[N][N];
int st[N][N];
int n,m;
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
int res=1;
void dfs(int x,int y){
st[x][y]=true;
for(int i=0;i<4;i++){
int xx=x+dx[i],yy=y+dy[i];
if(xx>=0&&xx<n&&yy>=0&&yy<m&&g[xx][yy]=='.'&&st[xx][yy]==false){
res++;
dfs(xx,yy);
}
}
}
int main(){
while(cin>>m>>n,n||m){
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>g[i][j];
}
}
int x,y;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(g[i][j]=='@') {
x=i;
y=j;
break;
}
}
}
memset(st,0,sizeof st);
res=1;
dfs(x,y);
cout<<res<<endl;
}
return 0;
}
示例2:
#include<bits/stdc++.h>
using namespace std;
const int N=25;
int n,m;
char g[N][N];
bool st[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dfs(int x, int y)
{
int cnt = 1;
st[x][y] = true;
for (int i = 0; i < 4; i ++ )
{
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= n || b < 0 || b >= m) continue;
if (g[a][b] != '.') continue;
if (st[a][b]) continue;
cnt += dfs(a, b);
}
return cnt;
}
int main(){
while(cin>>m>>n ,n||m){
for(int i=0;i<n;i++) cin>>g[i];
int x,y;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(g[i][j]=='@'){
x=i;
y=j;
}
}
}
memset(st,0,sizeof st);
cout<<dfs(x,y)<<endl;
}
return 0;
}
广搜
特点:
BFS是从进到远搜索的,因此通常可以利用BFS来搜索最短的步数
搜索过程:
首先需要把起点给压到队列里面去,接着每一轮判断队列是否为空,如果为空则结束BFS,不为空则取出队首元素a,将所有与a相邻的点判断是否被访问过,如果没有就压进队列中。(按层遍历,找到即最优,并且通常需要通过队列来实现)
#include<iostream>
using namespace std;
/*
5 4
0 0 1 0
0 0 0 0
0 0 1 0
0 1 0 1
0 0 0 1
1 1 4 3
*/
int map[51][51],book[51][51];//map存储地图,book存储是否被标记过
int m,n,sx,sy,p,q,tx,ty;//m行n列sx,sy表示悟空的坐标,p,q表示师傅所在的坐标
//tx和ty记录中间步骤坐标
int head=1,tail=1,flag=0;//head表示队首,tail表示队尾,flag标记是否找到师傅
int nt[4][2]={{-1,0},{0,1},{1,0},{0,-1}};//遍历上下左右四个方向
struct node{
int x,y,step;
}que[2501];//定义结构体数组存储当前搜索的x和y以及步数
void bfs(){
//记录起点位置
que[tail].x=sx;
que[tail].y=sy;
que[tail].step=0;
tail++;//队尾后移
book[sx][sy]=1;//标记已走过
//开始循环搜索
while(head<tail){
for(int i=0;i<4;i++){
//遍历四个方向
tx=que[head].x +nt[i][0];
ty=que[head].y+nt[i][1];
//判断是否出界
if(tx<1||ty<1||tx>m||ty>n) continue;
//判断该坐标是否可走
if(map[tx][ty]==0&&book[tx][ty]==0){
book[tx][ty]=1;//标记已经走过
//当前点入队
que[tail].x=tx;
que[tail].y=ty;
que[tail].step=que[head].step+1;
tail++; //队尾后移
}
}
//判断是否可以找到师傅
if(tx==p&&ty==q){//成功找到师傅
flag=1;
break;
}
if(flag) break;//找到师傅,停止搜索
head++;//队首后移,继续搜索
}
}
int main(){
cin>>m>>n;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
cin>>map[i][j];
}
}
cin>>sx>>sy>>p>>q;
bfs();
cout<<que[tail-1].step<<endl;
return 0;
}