C++算法——DFS

DFS我们称之为深搜,通常解决一些最大最长或者所有可能的问题,一般用递归来实现。因为深搜基本上会遍历每一个结果,但暴力法不同在于,深搜可以通过递归中不满足条件,实现剪枝。

我们先来看一道例题,感受一下深搜。

在这里插入图片描述

思路其实比较简单,有点类似于穷举法。我们构建一棵数,每一层依次增加一个没有枚举的数,当到达底层的时候就是一个答案。

在这里插入图片描述

比如我们先将第一个数填1,然后由于还有数未填入,继续深入到达下一层。下一层由于第一个数1已经枚举,我们枚举第二个数2。然后继续进入第三层,枚举第三个数3,由于已经把所有的数都枚举完,我们此时要进行一个回溯,回到第二层。由于第二层中只有一个数没有枚举,因此我们已经枚举完第二层的所有可能,因此回溯到第一层。接下来将第二位置填入未被枚举的3,以此反复。

在这里插入图片描述

#include<bits/stdc++.h>

using namespace std;

const int N = 10;

//用一个path数组来存储每次到底层的路径 
int path[N];
//用一个布尔数组来存储每次已经遍历的点,默认是false 
bool st[N];
int n;

//u表示当前的层数 
void dfs(int u)
{
    
    
    //当已经到达最底层了,溯回并输出路径 
    if( u == n )
    {
    
    
        for(int i = 0 ; i < n ; i++) printf("%d " , path[i] );
        //作用跟printf("%s\n",s),默认帮你换行 
        puts("");
        //溯回上一层 
        return;
    }
    else
    {
    
    
        //这里从第一个数开始循环 
        for(int i = 1; i <= n ; i++)
        {
    
    
            //如果该数字未被访问,就使用 
            if( !st[i] )
            {
    
    
                path[u] = i;
                //标记第i个数已经被使用 
                st[i] = true;
                //进入下一层 
                dfs( u + 1 );
                //还原现场 
                st[i] = false; 
            }
        }
    }

}

int main()
{
    
    
    cin >> n;

    dfs(0);

    return 0;
}

作者:阿柴
链接:https://www.acwing.com/activity/content/code/content/576222/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

深搜总结起来就是:

(1)先想好递归方程

(2)处理好枚举数据,对已遍历的数据要标记

(3)一定一定要还原现场,这是回溯的决定性条件

接下来我们再看一下一道难题:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//y总第一个解法,按列来枚举`
#include<bits/stdc++.h>

using namespace std;

const int N = 20;

char g[N][N];
bool col[N] , dg[N] , udg[N];
int n;

void dfs(int u)
{
    
    
    if( u == n )
    {
    
    
        for(int i = 0 ; i < n ; i++) puts( g[i] );

        puts("");

        return;
    }
    else
    {
    
    
        for(int i = 0 ; i < n ; i++)
        {
    
    
            //当满足列没有皇后,对角线没有皇后,反对角线没有皇后
            if( !col[i] && !dg[u + i] && !udg[n - i + u] )
            {
    
    
                //这一格子放置皇后
                g[u][i] = 'Q';
                //此时这一列,这一对角线,这一反对角线就不能放置皇后了
                col[i] = dg[u + i] = udg[n - i + u] = true;
               //递归到下一层 
                dfs(u + 1);
                //递归出来后返回上一层,并还原现场
                col[i] = dg[u + i] = udg[n - i + u] = false;
                //把皇后给做掉
                g[u][i] = '.';
            }
        }
    }
}

int main()
{
    
    
    cin >> n;

    for(int i = 0 ; i < n ; i ++)
    {
    
    
        for(int j = 0 ; j < n; j++) g[i][j] = '.';
    }

    dfs(0);

    return 0;
}

//第二种方法,遍历每一个格子
#include<bits/stdc++.h>

using namespace std;

const int N = 20;

char g[N][N];
bool row[N] , col[N] , dg[N] , udg[N];
int n;

//表示第x行第y列,放置了s个皇后
void dfs(int x , int y , int s)
{
    
    
    //当当前x行已经到达边界,转到下一行,列数归零
    if( y == n ) y = 0 , x++;

    //当到最后一个行,如果此时已经存在了n个皇后,就输出结果
    //为什么不判断y呢?因为最后一行只能放一个皇后
    if( x == n )
    {
    
    
        if( s == n )
        {
    
    
            for(int i = 0 ; i < n ; i++) puts( g[i] );
            puts("");
        }

        return;
    }


    //放皇后
    if( !row[x] && !col[y] && !dg[y + x] && !udg[y - x + n] )
    {
    
    
        g[x][y] = 'Q';

        row[x] = col[y] = dg[x + y] = udg[y - x + n] = true;

        dfs(x , y + 1 , s + 1 );

        row[x] = col[y] = dg[x + y] = udg[y - x + n] = false;

        g[x][y] = '.';
    }

    //不放置皇后
    dfs( x , y + 1 , s );
}

int main()
{
    
    
    cin >> n;

    for(int i = 0 ; i < n ; i ++)
    {
    
    
        for(int j = 0 ; j < n; j++) g[i][j] = '.';
    }

    dfs(0 , 0 , 0);

    return 0;
}

作者:阿柴
链接:https://www.acwing.com/activity/content/code/content/580140/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/MrChen666/article/details/109684097
今日推荐