八皇后时间复杂度_2698 八皇后问题

73e932cfd6f0b9949ea6cc5228f305bd.png

55b10493aac9e8db4c74f789f297e3b2.png

八皇后问题

题目:

在国际象棋棋盘上放置八个皇后,要求每两个皇后之间不能直接吃掉对方。

Input

无输入。

Output

按给定顺序和格式输出所有八皇后问题的解(见Sample Output)。

Sample Input

Sample Output

No. 1

1 0 0 0 0 0 0 0

0 0 0 0 0 0 1 0

0 0 0 0 1 0 0 0

0 0 0 0 0 0 0 1

0 1 0 0 0 0 0 0

0 0 0 1 0 0 0 0

Hint

此题可使用函数递归调用的方法求解。

四个方法(矩阵维护法、递归法、迭代法、手动堆栈法)

一、 题目分析

背景:八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n1×n1,而皇后个数也变成n2。而且仅当 n2 ≥ 1 或 n1 ≥ 4 时问题有解。

八皇后问题最早是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出。之后陆续有数学家对其进行研究,其中包括高斯和康托,并且将其推广为更一般的n皇后摆放问题。八皇后问题的第一个解是在1850年由弗朗兹·诺克给出的。诺克也是首先将问题推广到更一般的n皇后摆放问题的人之一。1874年,S.冈德尔提出了一个通过行列式来求解的方法,这个方法后来又被J.W.L.格莱舍加以改进。

思路:本题,我们只用讨论8*8的情况。很显然,我们可以用穷举法举出所有的矩阵情况,这不失为一种方法。同时,也很容易看出许多情况是不可能出现的,甚至是可以用边界值排除的。为了提高算法精炼程度,我们理应进行优化。

但在根本上,这还是可以看作是一个有条件的穷举算法。

二、 数据结构

static int gEightQueen[8] = { 0 }, gCount = 0;

一个数组记录每行皇后位置,数组序号代表行数。

整形变量代表可能出现的情形数目。

数据结构很简单,作者并没有将所有矩阵保留记录,而是选择性输出。

所以,在空间复杂度上,这个算法是极优的。

三、 算法及代码说明

1、 主函数

int main(int argc, char*argv[]) //主函数。

{

eight_queen(0);

cout << "total=" << gCount << endl;

return 0;

}

2、 运算主体

void eight_queen(int index)

{

int loop;

for (loop = 0; loop < 8; loop++)

{

if (check_pos_valid(index, loop))

{

gEightQueen[index] = loop;

if (7 == index) //递归结束条件,行数为8.

{

gCount++, print();

gEightQueen[index] = 0; //作用同下

return;

}

eight_queen(index + 1); //递归调用

gEightQueen[index] = 0; //进行一种情形后,将上一层调用函数数组元素归零,继续下一种情况。

}

}

}

3、 检验并输出

int check_pos_valid(int loop, int value)//检查是否存在有多个皇后在同一行/列/对角线的情况

{

int index; //跟上一个函数参数名重合,并且命名不形象,是该程序设计上的一大败笔。

int data;

for (index = 0; index < loop; index++)

{

data = gEightQueen[index];

if (value == data) //排除同列

return 0;

if ((index + data) == (loop + value)) // 排除斜线

return 0;

if ((index - data) == (loop - value)) //这个检验方式很有特点,包括两种斜线,还简练 ,值得学习。

return 0;

}

return 1;

}

void print()//输出每一种情况下棋盘中皇后的摆放情况

{

for (int i = 0; i < 8; i++)

{

int inner;

for (inner = 0; inner < gEightQueen[i]; inner++)

cout << "0";

cout <<"#";

for (inner = gEightQueen[i] + 1; inner < 8; inner++)

cout << "0";

cout << endl;

}

cout << "==========================n";

}

算法:

主程序:采用递归调用替代普通的循环,每一种情况对应一个递归调用。而且,利用返回的层级不同,避开已有的情况。

判断:除了形参的命名上有些失误,本身的判断语句简练的包括了三种可能的重复。

输出:利用数据结构本身的便利性,输出时只用了解皇后位置。

四、 算法分析

1、空间复杂度:这个程序,其最优异的点应该就在空间的把握上,简单的一维数组和整形,避免了二维数组的大量使用。抓住了本题的核心,只储存了皇后的位置,使输出很简单。

2、时间复杂度:可以从性能测试看出,递归调用要1964次,每次调用中要进行循环尝试,每次循环尝试要进行一次循环检查,每次检查的次数取决于index(行数)的大小,可知为15398。设每行尝试n次,循环尝试m次,检查p次,每一次运行次数是n*m*p,大体是在n的三次方量级。递归算法并没有相较于直接循环有质的飞跃。

下图是性能数据:

(1)平面输出图

0fdbcb77d929da96ae2f0d4d73a48d4a.png

(2)调用图

bffd5a5bf93bbcf65283ce3a200c0051.png

4、 对比

#include<iostream>

using namespace std;

int queen[9]={-1,-1,-1,-1,-1,-1,-1,-1,-1};

int count=0;

bool available(int pointi,int pointj){//判断某个皇后是否与已有皇后冲突

for(int i=1;i<pointi;i++){

if(pointj==queen[i])return false;//同一列拒绝

if((pointi-i)==(pointj-queen[i]))return false;//同一主对角线拒绝

if((pointi-i)+(pointj-queen[i])==0)return false;//同一副对角线拒绝

}

return true;

}

void findSpace(int queenNumber){//在第queenNumber行找能放皇后的位置

for(int i=1;i<9;i++){//从1~8遍历这一行的八个空位

if(available(queenNumber,i)){

//如果可以放这个位置就记录下第queenNumber个皇后的位置

queen[queenNumber]=i;

if(queenNumber==8){//如果八个皇后都放满了统计一下

count++;

return;

}

int nextNumber=queenNumber+1;//还有皇后没放递归放下一个皇后

findSpace(nextNumber);

}

}

queen[--queenNumber]=-1;//如果这一行没有可放的位置说明上一行皇后放的位置不行,要为上一个皇后寻找新的可放位置

return;

}

int main(){

findSpace(1);//从(1,1)开始递归好理解

cout<<count<<endl;

return 0;

}

/*---------------------

作者:codes_first

来源:CSDN

原文:https://blog.csdn.net/codes_first/article/details/78474226

版权声明:本文为博主原创文章,转载请附上博文链接

5、 其他方法

用循环代替递归,有同样效果,但是运行次数会更多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值