非常经典的一道题:

N皇后问题: 
国际象棋中皇后的势力范围覆盖其所在的行、列以及两条对角线,现在考察如下问题:如何在n x n的棋盘上放置n个皇后,使得她们彼此互不攻击 。

免去麻烦我们这里假定n不是很大。。。

(图片来自百度百科(这是8皇后问题的一种解法))

某leetcode大犇曾说过:“这个问题和解数独题目有一个很大的共同点,那就是:我都不会。”

好了下面开始分析:(废话警告)

初步判断这问题的特点有:

1.有个场地来放置单位。

2.各个单位之间有制约。

3.没有特殊的数学方法,得把某一个摆法摆出来才能判断是否可行。

于是萌新一般都会这么想:对于1,:我搞个二维数组来存。对于2:我搞个判定函数来一个一个判定。对于3:我暴力枚举。

那么算法框架大概就是:我对二维数组的所有情况进行枚举,然后对每种情况进行判定

over ,输个数字n,按下回车,双手离开键盘,等了老半天发现命令提示符只有个光标在跳动 |

如果是在做网题,说不定就判定是超时或者内存溢出,过不了。

然后就开始思考怎么优化:

对于1,我可以缩减吗····,改成一维数组,,,似乎徒增麻烦。

对于2,我可以有更巧妙的判断方法吗?·············有个鬼,还不得把每个棋子的每一行每一列每两个斜线都瞅一下。

对于3,我一定要把所有情况都举出来嘛?···(于是脑子里面开始摆起了棋子,模拟算法过程)

然后就会发现  比如第一行前两个格子一开始就摆了两个相邻的棋子,诶这不明摆着皇后互怼了吗!后面还继续枚举就太傻了吧···

     这种傻事情做了不是白白增加耗时吗!

      所以就思考如何在这种“傻情况”出现的时候就pass掉后面所有情况。。。

于是脑子陷入了一团乱麻。。。

打住!

咱们换个思考方式,既然直接想有点困难,不如我们想办法先做点处理,让这些玩意儿便于我们把弄。

再回头想想规则,皇后可以吃掉所处的一行一列以及斜线上的棋子,那也就是说,每一行都最多只能有一个棋子,每一列都最多只能有一个棋子,

那我们不妨把每一行看成一组!

那么这一组就只有n个可能性(一行n个位置 每一次只有一个位置被占用)

也就是单个一行有n种可能性

因为有n行

哈哈那么就瞬间只需要讨论n*n种可能性了!

还记得我们之前的思考吗?:

对于3,我一定要把所有情况都举出来嘛?···(于是脑子里面开始摆起了棋子,模拟算法过程)

     然后就会发现  比如第一行前两个格子一开始就摆了两个相邻的棋子,诶这不明摆着皇后互怼了吗!后面还继续枚举就太傻了吧···

   这种傻事情做了不是白白增加耗时吗!

   所以就思考如何在这种“傻情况”出现的时候就pass掉后面所有情况。。。

像这种情况:

(我的天这皇后画的...)

显然下面3,4,5····行都不用枚举了,直接pass掉

于是我们思路慢慢清晰了起来:

我们从第一行开始枚举,第一行第一格:

然后枚举第二行,也从第一格开始:

判定一下,哇,不行,咋办呢?这情况下面所有行都没有枚举的必要了,但是本行还是可以继续的。。。于是我们开始枚举第二行第二个情况:

不行(斜线上互吃)。

继续第二行下一个情况:

行嘞!

那么我们就可以开第三行了:

不行(一列上吃)。

不行(斜线上吃)

不行。

还不行

行嘞!

··················

好了,算法思路大概就出来了。

我们就这样干下去,直到最后一行可行,那么我们就获得了一个可行解了,

然后我们想继续获得其他解,那么继续枚举下去,但是要退一步:

先从倒数第一行开始,我们枚举下一个,有解则输出,无解则继续。

倒数第一行结束了,咋办?

别忘了倒数第二行的情况还不一定枚举完了呢。

于是退到倒数第二行,继续如上操作。

··················


干说着没用,撸代码:

#include<iostream>

using namespace std;
int main()
{
return ;
}

先准备好需要用的存储(这里用64个绰绰有余)

ans是用来存每一次的解 ans[1]表示第一行的那个元素的所在列的位置

 #include<iostream>

 using namespace std;
bool colMark[] = { };//元素所在列 如果有了元素在第k列 那么colMark[k]就标记上true
bool naMark[] = { };//捺,撇···顾名思义,表示元素所在的两个斜线o.o 原理同上
bool pieMark[] = { };
int total = ; //解的总个数
int N = ;
int ans[] = { };
int main()
{
return ;
}

然后下面先写一个显示函数:

 void showOneSolution()//用来显示一个解
{
total++;
for (int i = ; i<=N; ++i)
{
cout << ans[i] << " ";
}
cout << endl;
}

下面是核心:

void Dfs(int i)   //可以百度学习 深度优先搜索
{
if (i > N) //判断是否已经枚举完了N行
{
showOneSolution(); //枚举完了就输出(此刻我们处于N+1行)
return; //返回到第N行
}
for (int j = ; j <= N; ++j) //这里的j表示 本行的第j列
{
if ((!colMark[j])&&(!naMark[j-i+N])&&(!pieMark[i+j])) //这里的"j-i+N" "i+j"建议自己体会
{          //判断这个格子所在的两个斜线和所在列是否为空
colMark[j] = true;
naMark[j - i + N] = true;
pieMark[i + j] = true;
ans[i] = j;
Dfs(i + );
colMark[j] = false; //这个格子枚举完了要进入本行第j+1个格子(或者结束本行) 走之前别忘了恢复标记
naMark[j - i + N] = false;
pieMark[i + j] = false;
}
}
}

最后组装起来:(当然啦,还是建议自己撸代码,你会发现很多意想不到的问题~解决问题的同时也是进步)

 #include<iostream>

 using namespace std;
bool colMark[] = { };
bool naMark[] = { };
bool pieMark[] = { };
int total = ;
int N = ;
int ans[] = { };
void showOneSolution()//用来显示一个解
{
total++;
for (int i = ; i<=N; ++i)
{
cout << ans[i] << " ";
}
cout << endl;
}
void Dfs(int i)
{
if (i > N) //判断是否已经枚举完了N行
{
showOneSolution(); //枚举完了就输出(此刻我们处于N+1行)
return; //返回到第N行
}
for (int j = ; j <= N; ++j) //这里的j表示 本行的第j列
{
if ((!colMark[j]) && (!naMark[j - i + N]) && (!pieMark[i + j]))
{          //判断这个格子所在的两个斜线和所在列是否为空
colMark[j] = true;
naMark[j - i + N] = true;
pieMark[i + j] = true;
ans[i] = j;
Dfs(i + );
colMark[j] = false; // 走之前别忘了恢复标记
naMark[j - i + N] = false;
pieMark[i + j] = false;
}
}
}
int main()
{
cin >> N;
Dfs();
cout << total;
system("pause");
return ;
}

总结:

思路混乱不妨换个角度理一理。

在纸上面画一画算法过程会有不少帮助。

代码少出错还是得多撸。

想什么呢?!撸代码!

(新手向)N皇后问题详解(DFS算法)的更多相关文章

  1. BM算法  Boyer-Moore高质量实现代码详解与算法详解

    Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...

  2. SVD在推荐系统中的应用详解以及算法推导

    SVD在推荐系统中的应用详解以及算法推导     出处http://blog.csdn.net/zhongkejingwang/article/details/43083603 前面文章SVD原理及推 ...

  3. Java虚拟机详解04----GC算法和种类【重要】

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  4. Java虚拟机详解04----GC算法和种类

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  5. 八皇后问题---详解---参考<<紫书>>

    在一个8*8的棋盘上  放置八个皇后 , 使得他们互相不攻击(皇后攻击范围为 同行同列同对角线) , 方法一 : 从64个格子中 选一个子集 , 使得 " 子集 中恰好有八个元素 , 且任意 ...

  6. 详解zkw算法解决最小费用流问题

    网络流的一些基本概念 很多同学建立过网络流模型做题目, 也学过了各种算法, 但是对于基本的概念反而说不清楚. 虽然不同的模型在具体叫法上可能不相同, 但是不同叫法对应的思想是一致的. 下面的讨论力求规 ...

  7. 结合OPENSIFT源码详解SIFT算法

    平台:win10 x64 +VS 2015专业版 +opencv-2.4.11 + gtk_-bundle_2.24.10_win32 参考博客:https://www.cnblogs.com/cql ...

  8. 深入理解SVM,详解SMO算法

    今天是机器学习专题第35篇文章,我们继续SVM模型的原理,今天我们来讲解的是SMO算法. 公式回顾 在之前的文章当中我们对硬间隔以及软间隔问题都进行了分析和公式推导,我们发现软间隔和硬间隔的形式非常接 ...

  9. 详解rsync算法--如何减少同步文件时的网络传输量

    先看下图中的场景,客户端A和B,以及服务器server都保存了同一个文件,最初,A.B和server上的文件内容都是相同的(记为File.1).某一时刻,B修改了文件内容,上传到SERVER上(记为F ...

随机推荐

  1. clion 如何执行外部文件

    https://blog.csdn.net/he_yang_/article/details/96644480 这里这里

  2. Python字符串内建函数_上

    Python字符串内建函数: 注:汉字属于字符(既是大写又是小写).数字可以是: Unicode 数字,全角数字(双字节),罗马数字,汉字数字. 1.capitalize( ): 将字符串第一个字母大 ...

  3. PHP count_chars() 函数

    实例 返回一个字符串,包含所有在 "Hello World!" 中使用过的不同字符(模式 3): <?php高佣联盟 www.cgewang.com$str = " ...

  4. ajax模拟表单提交,后台使用npoi实现导入操作 方式一

    页面代码: <form id="form1" enctype="multipart/form-data"> <div style=" ...

  5. NodeJS 极简教程 <1> NodeJS 特点 & 使用场景

    NodeJS 极简教程 <1> NodeJS 特点 & 使用场景 田浩 因为看开了所以才去较劲儿.   1. NodeJS是什么 1.1 Node.js is a JavaScri ...

  6. luogu P5325 Min_25筛

    LINK:Min_25筛 新版感觉有点鬼畜 而且旧版的也够用了至少. 这个并不算很简单也不算很困难的知识点 学起来还是很麻烦的. (误入了很多dalao的blog 说的云里雾里的 甚是懵逼 这里推荐几 ...

  7. HTML与XHTML之间的区别

    HTML与XHTML之间的差别,粗略可以分为两大类比较:一个是功能上的差别,另外是书写习惯的差别.关于功能上的差别,主要是XHTML可兼容各大浏览器.手机以及PDA,并且浏览器也能快速正确地编译网页. ...

  8. 【机器学习的Tricks】随机权值平均优化器swa与pseudo-label伪标签

    文章来自公众号[机器学习炼丹术] 1 stochastic weight averaging(swa) 随机权值平均 这是一种全新的优化器,目前常见的有SGB,ADAM, [概述]:这是一种通过梯度下 ...

  9. [转]HashMap 和 currentHashMap 总结

    作者:浅蓝色的麻吉 https://www.jianshu.com/p/a7767e6ff2a2 1. 什么是哈希表 在讨论哈希表之前,我们先大概了解下其他数据结构在新增,查找等基础操作执行性能 1. ...

  10. 还在纠结学什么编程语言吗?Python可能会“教”你做人

    这几年为什么Python在中国就火起来了? Python这个东西国,大概是从2017年末开始,突然就火了起来的.此前,对于Python,乃至编程,绝大多数程度上都是专业人士的话题,在普通大众层面上起不 ...