简单的dfs

dfs(Depth_First_Search):

它是一种图的遍历形式,其具体意义是从图中的某个顶点v出发,不停的遍历v的各个临界点,然后从各个临界点开始继续的向四周发散,直至遍历完所有与v路径相通的点,究其本质其实是应用了一种递归的思想;

模板代码为:

void dfs()//参数用来表示状态  
{  
    if(到达终点状态)  
    {  
        ...//根据题意添加  
        return;  
    }  
    if(越界或者是不合法状态)  
        return;  
    if(特殊状态)//剪枝
        return ;
    for(扩展方式)  
    {  
        if(扩展方式所达到状态合法)  
        {  
            修改操作;//根据题意来添加  
            标记;  
            dfs();  
            (还原标记);  
            //是否还原标记根据题意  
            //如果加上(还原标记)就是 回溯法  
        }  
 
    }  
} 

对于dfs来说简单的应用有全排列,迷宫问题,或者结合剪枝,回溯,dp等算法;下面我们来介绍其具体应用;

1.dfs全排列问题:

#include<iostream>
#include<string.h>
using namespace std;
int a[101],b[101];
int i,j,n;
void  print()
{
    for(i=1;i<=n;i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
}
void dfs(int i)
{
    if(i==n+1)
    {
        print();
        return ;
    }
    for(int j=1;j<=n;j++)//int j是每一次都赋值,而j,则在递归过程中也会继续++; 
    {
        if(b[j]==0)
        {
            a[i]=j;
            b[j]=1;
            dfs(i+1);
            b[j]=0;
        }
    }
}
int main()
{
    cin>>n;
    memset(a,sizeof(a),0);
    memset(b,sizeof(b),0);
    dfs(1);
    return 0;
}

全排列问题就是一种典型的dfs问题,由于需要遍历所有的数字,而且不能重复,因而与dfs的不断发散的主题刚好契合,当然对于c++来说next_permutation是更简单的一种全排列,同时这种全排列的解决方法还可以与回溯法相结合解决典型的

n皇后问题:
n皇后的问题关键是所有的位置既不能同行,也不能同列,还不可以同对角线,比如第一行的第一列放棋子,下一行就只能在二至n列放置棋子同时需要减去对角线上的情况,即为n*(n-1)*(n-2)...*1再减去对角线上的情况,其实只是上题的代码加上一个判断条件:if(abs(pre[x]-x)==abs(pre[y]-y))return false;

void generate(int index)
{
    if(index==n+1)
    {
        res++;
        return;
    }
    for(int x=1;x<=n;x++)
    {
        if(hashTable[x]==true)//此行没下子;
        {
            bool flag=true;
            for(int pre=1;pre<index;pre++)
            {
                if(abs(P[pre]-x)==abs(pre-index))
                {
                    flag=false;
                    break;
                }
            }
            if(flag)
            {
            P[index]=x;
            hashTable[x]=true;
            generate(index+1);//继续下一列;
            hashTable[x]=false;//此次排列结束,回溯;    
            }
        } 
    }
}

3.迷宫类问题:对于迷宫类问题来说,若是求所有的可行路径就采用dfs,而求最短路径则bfs更加简便;

解决迷宫问题实际就是图的遍历思想,一步一步的发散寻路,到达一次终点res++,最终可以得到所有的路径。

题目:
从s到t,.意味着可以走,*意味着不能走,如果能走,输出路径,如果不能走,输出no

输入:

5 6
....S*
.***..
.*..*.
*.***.
.T....

输出:

....m*                                                                        
.***mm                                                                        
.*..*m                                                                        
*.***m                                                                        
.Tmmmm  

解决代码:

#include <iostream>
#include <string>
using namespace std;
int n, m;
string maze[110];
bool vis[110][110];
bool in(int x,int y){//防止走到地图外面
    return 0<=x&&x<n&&0<=y&&y<m;
}
bool dfs(int x,int y){//x代表行,y代表列
    if(maze[x][y]=='T'){
        return true;
    }
    vis[x][y]=1;//标记当前点已走过
    maze[x][y]='m';//走过的点用字符m标记
    int tx = x-1,ty=y;//向上搜
    if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
        if(dfs(tx,ty)){
            return true;
        }
    }
    tx=x,ty=y-1;//
    if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
        if(dfs(tx,ty)){
            return true;
        }
    }
    tx=x+1,ty=y;//
    if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
        if(dfs(tx,ty)){
            return true;
        }
    }
    tx=x,ty=y+1;//
    if(in(tx,ty)&&maze[tx][ty]!='*'&&!vis[tx][ty]){
        if(dfs(tx,ty)){
            return true;
        }
    }
    vis[x][y]=0;
    maze[x][y]='.';
    return false;
}
int main() {
    // 输入迷宫地图
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        cin >> maze[i];
    }
    int x,y;//确定起始位置
    for(int i=0;i<n;++i){
        for(int j=0;j<m;++j){
            if(maze[i][j]=='S'){
                x=i,y=j;
            }
        }
    }
    if(dfs(x,y)){
        for(int i=0;i<n;++i){
            cout<<maze[i]<<endl;
        }
    }
    else{
        cout<<"NO!"<<endl;
    }
    return 0;
}

但一般迷宫问题并不会出现DFS,因为对于迷宫问题来说有太多的不必要的走法需要记录,例如在i+1步到迷宫边界,那么回溯一步,继续向别处走也是一种迷宫的走法,一般DFS在迷宫中主要用于在BFS算法进行求解最短路径时,输出最短路的路径;

 void dfs(int x, int y) //递归打印
{
    if (x == 0 && y == 0)return;
    dfs(father[x][y].x, father[x][y].y);
//cout<<father[x][y].dir;
cout<<father[x][y].dir<<father[x][y].x<<father[x][y].y<<endl;;
}

猜你喜欢

转载自www.cnblogs.com/lszz/p/11827027.html