DFS与N皇后问题

DFS与N皇后问题

DFS

什么是DFS

DFS是指深度优先遍历也叫深度优先搜索

它是一种用来遍历或搜索树和图数据结构的算法

注:关于树的一些知识可以去看《树的概念及基本术语》这篇文章

它会不断地沿着节点的深度方向(该深度方向为其邻接点的方向)进行遍历

DFS如何实现

DFS主要步骤有以下几步

  • 访问并从某节点
  • 向邻接点出发,访问路径向深处走
  • 若走到最深处还有节点没访问,则再回到该层访问该节点(回溯)
  • 依次重复上述步骤,直至所有路径都被访问(递归)

DFS时空复杂度

空间复杂度

DFS算法实际上是一个递归算法,需要借助一个递归工作栈

  • 空间复杂度:O(n)

时间复杂度

  • 时间复杂度:不确定

为什么不确定?

因为遍历图的过程实质上是对每个顶点找查其所有邻接点的过程,其耗费时间取决于所采用结构

下面给出常用的几种数据结构DFS时的时间复杂度

(注:n是图中结点的个数,e是图中边的个数。)

  • 邻接表 O(e+n)
  • 邻接矩阵 O(n^2)

N皇后问题

N皇后问题(HDU2553)

HDU 2553 N皇后问题

在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。

输入:共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。

输出:共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。

输入样例:
1
8
5
0

输出样例:
1
92
10

经典DFS带来的问题

而经典的DFS,基本是将所有子节点全部扩展开,再选取最新的节点进行扩展。

但是对于N皇后来说,我们如果一一枚举,则有N的N次方种情况.

即使N=10,则有10^10种情况,我们还要在这些情况中进行筛选,这无疑需要巨大的计算

故:我们需要对DFS进行优化

回溯与减枝

我们可以让程序在某节点由某些条件就可以判断再进行继续展开并不符合要求,然后立即返回,达到回溯和减少树的枝干的生成(减枝)的目的,从而进行优化。

下面给出N=4时减枝的树状图解

图片来自百度

问题分析

关键问题

关键问题:在扩展节点时如何去掉不符合条件的节点?

设左上角是(0,0),已经放好皇后是(i,j),不同行、列、斜线的新皇后是(r,c),则:

  • 横向不同行 i != r
  • 纵向不同列 j != c
  • 斜对角:从(i,j)向斜对角走a步,新坐标(r,c)有4种情况 (i-a,j-a) (i+a,j-a) (i-a,j+a) (i+a,j+a),即|i-r| = |j-c|。也就是说不再斜线上满足 |i-r| != |j-c|

其他问题

  • 由于给出N<=10,所以可以用打表的方式,将每种n取值对应的结果放到数组ans[n]中
  • 对于数组ans[]由于大小最方便设为11,n在0--10中取值一共11种情况。尽管我们可以忽略0定义成大小为10的数组(将n=1结果存放在ans[0]以此类推),但我们在取出数据时还要进行减法运算并不方便。
  • 对于在对特定的n取值分析时可用个tot变量来记录到达最深处情况的个数,由于循环是从0开始,我们只需要让r=n时tot自增1来记录并回溯
  • 数组col[r] = c,表示皇后放在第r行c列,我们可以用col[i] 来依次获得前几行皇后位置信息,故对于本题col[]数组定义最大为10就行

题解代码

#include <iostream>

using namespace std;

//tot记录每次n取特定值能放置n皇后情况的个数,每当n改变时tot重置为0
int n, tot = 0;

//表示皇后存放位置 col[r] = c 表示第r行的皇后在第c列
int col[10];

//r为行,c为列
//check()用于检查新放置的皇后(r,c)是否和已经存放好的皇后冲突
//冲突返回false,不冲突返回true
bool check(int r, int c)
{
    for (int i = 0; i < r; i++)
        if (col[i] == c || (abs(col[i] - c) == abs(i - r))) //绝对值判断是否在同一斜线上
            return false;   //冲突返回false
    return true;    //不冲突返回true
}

//r为检索的行
//对每行检索并放置皇后
void DFS(int r)
{
    //若达到最底行(层)则新增1种情况,并返回(回溯)
    if (r == n) {
        tot++;
        return;
    }

    //对第r行(层)的每一列进行检索,若能放置则进入下一行(层)再从r+1行第0列向后检索放置依次类推
    //若不能放置则本层次结束,递归然后回溯出去再改变上一层的列数,从该列再进行展开
    for(int c=0;c<n;c++)
        if (check(r, c))    //检查若放在该层该列是否与之前皇后冲突
        {
            col[r] = c;     //记录再第r行c列放皇后,用于下一层向上检验冲突
            DFS(r + 1);     //继续放下一层皇后
        }

}



int main()
{
    //用于打表记录结果的数组
    int ans[11];

    memset(ans, 0, sizeof(0));

    //算出n取所有值的答案
    for (n = 0; n <= 10; n++)
    {
        memset(col, 0, sizeof(col));    //清空上一回的数组,计算下一个n皇后问题
        tot = 0;
        DFS(0);
        ans[n] = tot;   //打表,保存
    }

    while (cin >> n)
    {
        if (n == 0)
            return 0;
        cout << ans[n] << endl;
    }

    return 0;
}

数据

下面给出n从1到10取值的数据以供参考

n取1的结果为:1
n取2的结果为:0
n取3的结果为:0
n取4的结果为:2
n取5的结果为:10
n取6的结果为:4
n取7的结果为:40
n取8的结果为:92
n取9的结果为:352
n取10的结果为:724

复杂度

以下对于本题解法而言

  • check()复杂度 O(n!)
  • DFS()复杂度 O(N)
  • 总复杂度 O(N!·N)

对于N>11且N<=15可用数据结构舞蹈链较快解决

数学性质

  • N皇后问题在数学上是个NP完全问题,不存在多项式时间算法

本文参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357

推荐阅读更多精彩内容