这周正在学习DFS和BFS。
体验中觉得这两个算法还是挺好用的,但是遇到某些数据大的情况就很容易超时。(所以到后面还是得学习一下如何优化,或者采用更佳的搜索方法来解决问题)
然后学习了一段时间,感觉基本上了解了DFS和BFS的基础实现原理以及应用(不过我认为还是得通过做题来培养自己的感觉,什么时候该采取DFS,什么时候该采用BFS),它们两者间各自的优势需要通过实际的问题来具体分析,根据它们各自的特点来应用于不同的问题中才能获得最优的性能。
总的来说,两者都是搜索方法 (这不是废话嘛!) ,但是两者的搜索方式却有极大的区别。
我将DFS比作莽夫,就是一条路莽下去,如果有分支路,就选择其中一条走下去,直到走到终点或者该点处没有路可以走了,这时候只能回头,走之前没有走过的分支,继续莽下去。
而BFS更像是水流。我们在最上层倒水下来,水会流向这个节点处的各个分支,逐层地开始搜索。
由于DFS的特性,我们会采用 递归的方式来实现。
具体模板:
void dfs()//参数用来表示状态,例如临界条件,达到终点的条件
{
if(到达终点状态)
{
...//根据题意添加
return;
}
if(越界或者是不合法状态)
return;
if(特殊状态)//剪枝(避免搜索不必要的地方)
return ;
for(扩展方式)
{
if(扩展方式所达到状态合法)
{
修改操作;//根据题意来添加
tag标记;
dfs();
还原tag标记; // dfs很重要一点就是可能要采用回溯思想,视题目而定
}
}
}
例题: HDU 1241 油田问题
AC代码如下:
#include <iostream>
using namespace std;
const int maxn=1e3;
char a[maxn][maxn]; // 用来储存油田矩阵
int dir[8][2]={{0,1},{0,-1},{1,0},{-1,0},{1,1},{-1,1},{1,-1},{-1,-1} }; //八个方向
int m,n; //m是行 n是列
void dfs(int x,int y)
{
int tx,ty;
for(int i=0;i<8;i++)
{
tx=x+dir[i][0]; //tx,ty分别是 下一个坐标代表的x,y;
ty=y+dir[i][1];
if( tx>=0 && tx<=m && ty>=0 && ty<=n ) //说明没有越界那么就继续搜下去
{
if(a[tx][ty]=='@') //使@变成*
{
a[tx][ty]='*';
dfs(tx,ty); //继续dfs深搜
}
}
}
}
int main()
{
while( cin>> m >> n && m && n )
{
for(int i=0;i<m;i++)
cin>>a[i];
int sum=0;
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
{
if(a[i][j]=='@') //如果遍历到一个@ 说明找到一个油田块
{
a[i][j]='*';
sum++;
dfs(i,j); //消除油田
}
}
cout<<sum<<endl;
}
return 0;
}
BFS 基本实现就采用 队列的方式
queue< pos > q;
// pos是一个struct 带有 x,y坐标 (用来表示二维数组的下标)
q.push(首节点);
while(!q.empty())
{
pos temp=q.front();
q.pop();
isok[temp.x][temp.y]=true; //标记该点已经走过了
for(int i=0;i<4;i++)
{
int tx=temp.x+dir[i][0];
int ty=temp.y+dir[i][1];
if( tx < n && tx>=0 && ty >=0 && ty < m && !isok[tx][ty] && s[tx][ty]=='.')
{
isok[tx][ty]=!isok[tx][ty]; //标记该点已经走过了
q.push(pos(temp.x+dir[i][0],temp.y+dir[i][1])); // 将这个节点下的子节点插入至队列中。
}
}