DFS与BFS对比
我们假设一个节点衍生出来的相邻节点平均的个数是N个,那么当起点开始搜索的时候,队列有一个节点,当起点拿出来后,把它相邻的节点放进去,那么队列就有N个节点,当下一层的搜索中再加入元素到队列的时候,节点数达到了N2,你可以想想,一旦N是一个比较大的数的时候,这个树的层次又比较深,那这个队列就得需要很大的内存空间了。
于是广度优先搜索的缺点出来了:在树的层次较深&子节点数较多的情况下,消耗内存十分严重。广度优先搜索适用于节点的子节点数量不多,并且树的层次不会太深的情况。
那么深度优先就可以克服这个缺点,因为每次搜的过程,每一层只需维护一个节点。但回过头想想,广度优先能够找到最短路径,那深度优先能否找到呢?深度优先的方法是一条路走到黑,那显然无法知道这条路是不是最短的,所以你还得继续走别的路去判断是否是最短路?
于是深度优先搜索的缺点也出来了:**难以寻找最优解,仅仅只能寻找有解。**其优点就是内存消耗小,克服了刚刚说的广度优先搜索的缺点。
(参考)
深度优先搜索(DFS)
入门例题一: 水池的个数
输入
第一行输入一个整数N,表示共有N组测试数据
每一组数据都是先输入该地图的行数m(0<m<100)与列数n(0<n<100),
然后,输入接下来的m行每行输入n个数,表示此处有水还是没水
(1表示此处是水池,0表示此处是地面)
输出
输出该地图中水池的个数。
每个水池的旁边(上下左右四个位置)如果还是水池的话的话,它们可以看做是同一个水池。
样例输入
2
3 4
1 0 0 0
0 0 1 1
1 1 1 0
5 5
1 1 1 1 0
0 0 1 0 1
0 0 0 0 0
1 1 1 0 0
0 0 1 1 1
样例输出
2
3
这是一道DFS的题目,简单来说就是不撞南墙不回头。用DFS对每个点的上下左右搜索,如果有水(==1)那么这个地方标记为0。
一趟搜索下来,我们可以把跟这个点连接的水池全部标记为0,然后给计数器变量加1,代表这整个为1个水池。
只要对所有点都进行一遍dfs,则可以求出所有水池数目。
#include <bits/stdc++.h>
#define N 105
using namespace std;
int arr[N][N];
void dfs(int a,int b)
{
if(arr[a-1][b]==1){arr[a-1][b]=0,dfs(a-1,b);} //向上搜索
if(arr[a+1][b]==1){arr[a+1][b]=0,dfs(a+1,b);} //向下搜索
if(arr[a][b-1]==1){arr[a][b-1]=0,dfs(a,b-1);} //向左搜索
if(arr[a][b+1]==1){arr[a][b+1]=0,dfs(a,b+1);} //向右搜索
}
int main(void) // 传入void
{
int t;
int n,m;
cin>>t;
while(t--)
{
int cnt=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>arr[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(arr[i][j]==1) //代表此点为水池
{
cnt++;
dfs(i,j); //将与该水池相邻的水池都标记为0
}
}
cout<<cnt<<endl;
}
return 0;
}
分析:
例如输入:
3 4
1 0 0 0
0 0 1 1
1 1 1 0
当i=1,j=1的时候,有一个水池(==1),那么进入if语句,计数器加一,然后开始搜索i=1,j=1周边有没有水池(结果是没有),所以继续进行for循环。
当到i=2,j=3的时候,又有一个水池,进入if,计数器此时为2了,开始搜索i=2,j=3周边,结果右边和下面都有,然后把右边和下面标为0(防止之后主函数的for循环==1中计数器再加,因为连接的水池都看作一个)然后开始搜索那个右边和下面的,之后同理。
可以发现,这样搜索的话,只要旁边有水池,dfs()函数就可以把它槟城0,这样在下面的循环中,计数器就不会再加了,达到了连接的水池都看作一个的目的。
入门例题二:N皇后问题
链接在这
(下文都是参考上面链接滴)
在一个 N × N 的棋盘上放置 N 个皇后,每行刚好放置一个并使其不相互攻击(同一行、同一列、同一斜线上的皇后都是自动攻击),求一共有多少种合法的方法放置 N 个皇后 。
输入格式:输入一个整数N,代表N个皇后。
输出格式:输出一个整数,表示有多少种放置皇后方法。
设置三个数组,一个用来储存列(只需要遍历行,不需要遍历列,否则会出现行和列都同时存在皇后的情况)两个用来储存对角线。
于是,对于对角线1(从右上到左下),我们可以表示为 (行 + 列);
同理,对于对角线2(从左上到右下)可以表示为(行 - 列 + n)。
代码如下:
//N皇后
#include<bits/stdc++.h>
using namespace std;
int n, ans;
bool vy[1005], vd1[1005], vd2[1005];
bool check(int x, int i)
{
return !vy[i] && !vd1[x + i] && !vd2[x - i + n];
} //检查这个地方能不能放的函数,如果三个地方都是False(三个地方都能放),那么return true
void dfs(int x)
{
if(x == n)
{
//从0行开始的,所以当 x = n 时候就结束
ans++;
return;
}
for(int i = 0; i < n; i++)
{
//每一行内,对列情况讨论(每一个小 path)
if(check(x, i))
{
//如果可以放置
vy[i] = true;
vd1[x + i] = true;
vd2[x - i + n] = true;
dfs(x + 1); //进行下一行搜素
//搜索完成后需要释放掉
vd1[x + i] = false;
vd2[x - i + n] = false;
vy[i] = false;
}
}
}
int main()
{
cin>>n;
dfs(0);
cout<<ans;
return 0;
}
待补,真的不是很懂TT