深搜广搜简单题目总结

目录

        深搜(DFS):

                1.寻找路径

                2.求连通块

        广搜(BFS): 

                求最短路径

        总结


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

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

深搜(DFS):

1.寻找路径

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

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

深搜广搜简单题目总结_第1张图片

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

代码:

#include
#include

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]<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块数

深搜广搜简单题目总结_第2张图片

如图,共有4块W

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

//错误代码
#include
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
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
#include
//BFS算法 奇怪的电梯
 
using namespace std;
 
bool visited[205];//记录是否已经到过这层,最短路径问题,到走过的楼层无意义
 
struct node//楼层节点
{
	int now;//当前层数
	int count;//当前启用电梯次数
};
 
queueQ;//用来保存当前楼层节点,先进先出
 
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
#include
#include
#include
#define manown 100001
using namespace std;

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

void bfs(int x, int y)
{
	memset(dis, -1, sizeof(dis));
	queueq;
	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,这时,我们应该进一步考虑搜索的优化,是不是可以进行一些剪枝?怎样进行一些剪枝?

你可能感兴趣的:(算法,bfs,dfs,c++,c语言,算法)