【算法模板】dfs 八皇后问题

1.前言

 本文将以经典的八皇后问题来解析dfs的主要思想。

2.题目

题目出处:活动 - AcWing

 3.思路讲解

dfs的思想暗含树的历遍,主要步骤为:

判断是否搜索完毕---历遍寻找符合条件的元素---递归进入下一层搜索---还原现场

 我们可以先分析这个问题,发现皇后在每一行只能有一个,并且对角线,反对角线,每一排,每一列都只有一个皇后。

那么我们就可以从底层出发,遍历一排中的每一个元素,然后深入搜索下一排符合条件的元素,搜索到最后时,即是符合条件的第一个答案,回溯到第一排的下一个元素进行搜索。

由于dfs的灵魂是递归,所以递归的知识一定要掌握清楚。

4.代码实现

 参照以下网格来理解代码:

 

 

 该方法符合dfs的一般做法,当然算法是死的,人是活的,依照不同的题目,dfs的参数,形式等多种多样,不敢一板子把代码论死。要参照思想,具体情况具体分析,dfs最重要的便是顺序!!!

4.1 法1

#include <iostream>
using namespace std;
const int N = 20;

//定义图(网格)
char path[N][N];

//col记录每一列的皇后数,确保只有1个
//dg记录正对角线的皇后数,确保只有1个
//udg记录反对角线的皇后数,确保只有1个
bool col[N],dg[N],udg[N];

//网格数n
int n;

void dfs(int u)
{
    //搜索完毕,并且符合要求时打印
    if(u==n)
    {
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < n; j++)
            {
                cout<<path[i][j];
            }
            cout<<endl;
        }
        cout<<endl;
        return;
    }
    
    //y代表y轴上的点
    for(int y = 0; y < n; y++)
    {
        //如果一列里面没有元素,对角线上没有元素,那么放入皇后Q;
        if(!col[y]&&!dg[y+u]&&!udg[y-u+n])
        {
            path[u][y]='Q';
            col[y]=dg[y+u]=udg[y-u+n]=true;
    
            //皇后数+1,进入下一层搜索
            dfs(u+1);

            //回溯到原始状态
            col[y]=dg[y+u]=udg[y-u+n]=false;
            path[u][y]='.';
        }
    }
    
}

int main()
{
    cin>>n;
    //先初始化网格,再由dfs确定皇后位置
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            path[i][j]='.';
        }
    }
    
    //从第0层开始(初始皇后数为0)
    dfs(0);
    
    return 0;
}

这里解释一下对角线的判定:

由直线方程y=(±)x+b;

我们想要一条对角线上的皇后只有一个,而一条直线上,截距是一样的,b = y ± x ;

但是由于数组的下标没有负数, y - x 有可能是负数,所以加上n,映射成正数!

如果(u)dg[ b ] 为真,那么该直线上已经有一个皇后。

4.2 法2

 

#include <iostream>
using namespace std;
const int N = 20;
char path[N][N];
//判断行,列,对角线
bool row[N],col[N],dg[N],udg[N];
int n;

void dfs(int x,int y,int s)
{
    //初始化条件
    if(y == n)
    {
        x++;
        y = 0;
    }
    if(x == n)
    {
        //搜素完毕,并且符合要求的条件
        if(s==n)
        {
            for(int i = 0; i < n; i++)
            {
                puts(path[i]);
            }
            puts(" ");
        }

        return;
    }
    
    //初始化网格
    path[x][y]='.';
    dfs(x,y+1,s);
    
    //思路同法1
    if(!row[y]&&!col[x]&&!dg[x+y]&&!udg[y-x+n])
    {
        row[y]=col[x]=dg[x+y]=udg[y-x+n]=true;
        path[x][y]='Q';
        dfs(x,y+1,s+1);
        row[y]=col[x]=dg[x+y]=udg[y-x+n]=false;
        path[x][y]='.';        
    }
    
}
int main()
{
    cin>>n;
    dfs(0,0,0);
    return 0;
}

但是法2在效率上远不如法1,因为在初始化回溯时进行搜索,浪费了很多不必要的搜索,只有当x,y回溯到0时才真正起到作用。

举此法是为了说明dfs的灵活性。

5.结语

 作为图论的必备入门技巧,要把握dfs的思想,并且熟练运用。

其实dfs更像一种循环,在if条件下找到不同层数的答案,完成搜索后继续下一个搜索,在不影响原来的数据的情况下输出答案。

后续会更新bfs的有关内容。

  • 14
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Laxinues

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值