深搜广搜简单题目总结

目录

        深搜(DFS):

                1.寻找路径

                2.求连通块

        广搜(BFS): 

                求最短路径

        总结


知识本身没有价值,只有思考和实践才让知识拥有价值

学习了深搜和广搜,它们可以解决哪些问题?

深搜(DFS):

1.寻找路径

例(详见洛谷P6207):如图,在有些地方不能通过的矩阵中找出从(1,1)坐标点到目标坐标点的路径并输出

'.'表示能通过,'*'表示不能通过

这种让我们寻找任意路径的题,我们可以轻松用深搜解决

代码:

#include<iostream>
#include<algorithm>

using namespace std;

int r, c;
char a[120][80];//盘面
bool visited[120][80];//是否访问过
int path[100100];//记录路径最后输出
int counts=0;

int dx[] = {-1,1,0,0};
int dy[] = {0,0,-1,1};

void display()
{
	for (int i = 0; i < counts; i+=2)
		cout << path[i] << ' ' << path[i + 1]<<endl;
}

void dfs(int x,int y)
{
	path[counts++] = x;
	path[counts++] = y;
	visited[x][y] = 1;
	if (x == r && y == c)
	{
		display();
		return;
	}
	int newx, newy;
	for (int i = 0; i < 4; i++)
	{
		newx = x + dx[i];
		newy = y + dy[i];
		if (newx<1||newx>r||newy<1||newy>c||a[newx][newy] == '*'||visited[newx][newy])
			continue;
		dfs(newx, newy);
        //回溯
		path[--counts] = 0;
		path[--counts] = 0;
	}
}

int main()
{
	cin >> r >> c;
	for(int i=1;i<=r;i++)
		for (int j = 1; j <= c; j++)
		{
			cin >> a[i][j];
		}
	dfs(1, 1);
}

这种问题还有很多,比如著名的八皇后,也是这种问题

2.求连通块

例(详见洛谷P1596):寻找矩阵中八个方向相连的W块数

如图,共有4块W

我们可以对W的位置且没有被搜索过的位置进行深搜,最终得到的和即为W连通块数

//错误代码
#include<iostream>
using namespace std;
//只要搜索与水相连的并且标记水即可(把水去掉)

int n, m;
int ans;//水坑数
char a[105][105];
bool visited[105][105];//是否记录过

int dx[] = { -1,-1,-1,0,0,0,1,1,1 };
int dy[] = { -1,0,1,-1,0,1,-1,0,1 };

int dfs(int x,int y)
{
	visited[x][y] = 1;
	for (int i = 0; i < 9; i++)
	{
		x += dx[i];
		y += dy[i];
		if (x<1 || x>n || y<1 || y>m||a[x][y]=='.'||visited[x][y]==1)
			continue;
		dfs(x, y);		
	}
	return 0;
}

int main()
{
	cin >> n >> m;
	for(int i=1;i<=n;i++)
		for (int j = 1; j <= m; j++)
		{
			cin >> a[i][j];
		}
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			if (a[i][j] == 'W'&&!visited[i][j])
			{
				ans++;
				dfs(i, j);
			}
		}
	
	cout << ans;
}

咦?为什么不对,这题调试了很久,最终发现,dfs里需要新建变量newx和newy,不能直接对x,y进行操作,这样回溯时,x和y最初的值就再也找不到了

同时我们并不一定需要visited数组判断当前W是否被记录过,在我们对W深搜时,我们可以直接把水'W'改为旱地'.',这样就保证了只有属于不同连通块的W才能被深搜

正确代码如下:

#include<iostream>
using namespace std;
//只要搜索与水相连的并且标记水即可(把水去掉)

int n, m;
int ans;//水坑数
char a[105][105];
//bool visited[105][105]; 

int dx[] = { -1,-1,-1,0,0,0,1,1,1 };
int dy[] = { -1,0,1,-1,0,1,-1,0,1 };

int dfs(int x,int y)
{
	a[x][y] = '.';//记录过的'W'变成旱地
	int newx, newy;
	for (int i = 0; i < 9; i++)
	{
		newx = x + dx[i];
		newy = y + dy[i];
		if (newx<1 || newx>n || newy<1 || newy>m||a[newx][newy]=='.')
			continue;
		dfs(newx, newy);		
	}
	return 0;
}

int main()
{
	cin >> n >> m;
	for(int i=1;i<=n;i++)
		for (int j = 1; j <= m; j++)
		{
			cin >> a[i][j];
		}
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			if (a[i][j]=='W')
			{
				ans++;
				dfs(i, j);
			}
		}
	cout << ans;
}

广搜(BFS):

求最短路径

广搜是一层一层向下搜索,所以广搜往往可以让我们找到最先成立的情况,也就是最短路径

例(详见洛谷P1135):有n层楼,每层楼上有一个数字,可以从当前层乘电梯上或下这个数字的层数,求从A层到B层的最短按键次数

比如第一行n层从A到B,第二行每层上的数字,我们这样表示:

结果为3

广搜常常借助队列来完成,将每层数依次放入队列,依次取出,就能保证一层一层,层层递进

代码:

#include<iostream>
#include<queue>
//BFS算法 奇怪的电梯
 
using namespace std;
 
bool visited[205];//记录是否已经到过这层,最短路径问题,到走过的楼层无意义
 
struct node//楼层节点
{
	int now;//当前层数
	int count;//当前启用电梯次数
};
 
queue<node>Q;//用来保存当前楼层节点,先进先出
 
int kk[205];//第i层上的number
 
int main()
{
	int n, a, b;
	cin >> n >> a >> b;
	for (int i = 1; i <=n; i++)
		cin >> kk[i];
	Q.push(node{ a, 0 });//入队
	while (not Q.empty())
	{
		node u = Q.front();//提取队首元素
		Q.pop();//弹出队首元素
 
		if (u.now == b)//是目标楼层,结束
		{
			cout << u.count;
			return 0;
		}
 
		int df = u.now + kk[u.now];//上楼
		if (df <= n && not visited[df])//是否能走
		{
			visited[df] = true;
			Q.push(node{ df,u.count + 1 });
		}
 
		df = u.now - kk[u.now];//下楼
		if (df >= 1 && not visited[df])//是否能走
		{
			visited[df] = true;
			Q.push(node{ df,u.count + 1 });
		}
		
	}
	cout << "-1";
	return 0;
}

又如(详见洛谷P1588):初始位置为x,目标位置为y,每一次可以前进一步、后退一步或者直接走到2*x的位置,求至少需要几步到达目标位置

依然是求最短路径问题

代码:

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#define manown 100001
using namespace std;

int dis[manown];//步数&记录

void bfs(int x, int y)
{
	memset(dis, -1, sizeof(dis));
	queue<int>q;
	dis[x] = 0;
	q.push(x);
	while (!q.empty())
	{
		int now = q.front();
		q.pop();

		if (now == y)
		{
			cout << dis[now] << endl;
		}
		if (now - 1 > 0 && dis[now - 1] == -1)
		{
			dis[now - 1] = dis[now] + 1;
			q.push(now - 1);
		}
		if (now + 1 < manown && dis[now + 1] == -1)
		{
			dis[now + 1] = dis[now] + 1;
			q.push(now + 1);
		}
		if (now * 2 < manown && dis[now * 2] == -1)
		{
			dis[now * 2] = dis[now] + 1;
			q.push(now * 2);
		}
	}
}

int main()
{
	int n;
	cin >> n;
	while (n--)
	{
		int x, y;
		cin >> x >> y;
		bfs(x, y);
	}
}

总结

无论是深搜还是广搜,其实都是搜索的一种方式,有时,比如连通块问题,深搜和广搜其实都能胜任,我们应该择优而行之。

最后,在一些题目中(例如背包问题),仅仅使用深搜或者广搜不是最好的办法,甚至都不能AC,这时,我们应该进一步考虑搜索的优化,是不是可以进行一些剪枝?怎样进行一些剪枝?

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在提供的AC代码中,有关于图的深度优先索(DFS)和广度优先索(BFS)的实现方法。 在第一段代码中,是一个关于深度优先索的实现。该代码通过递归的方式来实现深度优先索。它使用了一个路径数组和一个标记数组来记录路径和访问状态。在每一次递归时,根据条件进行索,并在索完成后进行回溯操作。最终输出所有路径的结果。 在第二段代码中,是一个关于图的广度优先索的实现。该代码使用了一个队列来进行广度优先索。它使用了一个二维数组来表示图的状态和一个二维数组来记录访问状态。在索时,将起始点入队并标记为已访问,然后通过循环遍历队列中的元素,并根据条件进行索,并将符合条件的点入队并标记为已访问。最终返回从入口到出口的路径长度。 因此,根据提供的AC代码,可以实现图的深度优先索和广度优先算法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [【C++】数据结构之深搜+广 经典题型](https://blog.csdn.net/weixin_64967734/article/details/124994451)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值