N后问题算法
一、实验目的及要求
所要涉及或掌握的知识:
1. 了解皇后相互攻击的条件:如果任意两个皇后在同一行,同一列或同一对角线,则她们相互攻击。
2. 运用迭代的方法实现6皇后问题,求解得到皇后不相互攻击的一个解
3. 在运用迭代的方法实现编程时,要注意回溯点
二、问题描述及实验内容
对6皇后问题求解,用数组c[1…6]来存皇后的位置。
c[i]=j表示第i个皇后放在第j列。
最后程序运行的结果是c[1…6]={1,5,8,6,3,7 }
三、问题分析和算法描述
6-QUEENS的算法表示:
输入:空。
输出:对应于6皇后问题的解的向量c[1…6]={1,5,8,6,3,7}
1. for k=1 to 6
2. c[k]=0 //没有放皇后
3. end for
4. flag=false
5. k=1
6. while k>=1
7.while c[k]<=5
8.c[k]=c[k]+1
9.if c为合法着色 then set flag=ture 且从两个while循环退出
10.else if c是部分解 then k=k+1
11.end while
12. c[k]=0 //回溯并c[k]=0
13. k=k-1
14. end while
15. if flag then output c
16. else output “no solution”
四、具体实现
# include <math.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include "iostream"
using namespace std;
int total = 0; //方案计数
void backtrace(int queen[],int N)
{
int i, j, k;
cout<<"★为皇后放置位置\n";
for (i=1;;)
{ //首先安放第1行
if(queen[i]<N)
{ //皇后还可调整
k=0; //检查与第k个皇后是否互相攻击
while(k<i&&abs(queen[k]-queen[i])&&(abs(queen[k]-queen[i])-abs(k-i))) k++;
if (k<=i-1)
{ //与第k个皇后互相攻击
queen[i]++; //第i个皇后右移一列,重测
continue;
}
i++; //无冲突,安置下一行皇后
if(i<N) continue;
cout<<"第"<<total+1<<"种为:\n";
for(int i=0;i<N;i++)
{
for(int j=0;j<queen[i];j++)
cout<<"□";
cout<<"★";
for(int k=queen[i]+1;k<N;k++)
cout<<"□";
cout<<endl;
}
total++; //方案数加1
if(total%5==0) cout<<endl;
queen[N-1]++; // 将第8个皇后右移一列,前8个不动
i=N-1; //此处是制造机会,如不成功则回溯,关键一步
}
else //当前行皇后无法安置,回溯
{
queen[i]=0; //当前行皇后回归1列
i--; //回溯到前一行皇后
if(i<0)
{ //回溯到数组1行之前,结束
cout<<"\n针对 "<<N<<" 皇后问题,"<<"一共找到 "<<total<<" 种放置方法。
"<<endl;
cout<<endl;
return;
}
else queen[i]++; //前一行皇后右移一列
}
}
}
void main()
{
while(1)
{
clock_t start, finish;
double duration;
int N;
cout<<"请输入皇后个数:"<<endl;
cin>>N;
int* queen=new int[N];
for (int i=0;i<N;i++) queen[i] = 0; //八皇后全放在第0列
int n=N; /* 定义数组p[N]用来存放结果序列,n为行号 */
start=clock();
total=0;
backtrace(queen,N);
finish=clock();
duration=(double)(finish-start);
cout<<"为找出放置方法系统共耗时 "<<duration/1000<<" 秒!\n"<<endl;
delete []queen;
}
}
运行结果:
回溯法求解八皇后问题
2010-10-29 18:28:56| 分类:算法分析 | 标签:皇后回溯数组位置八皇后问题|举报|字号订阅
问题描述:八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。
问题历史:八皇后问题最早是由国际象棋棋手马克斯·贝瑟尔于1848年提出。
之后陆续有数学家对其进行研究,其中包括高斯和康托,并且将其推广为更一般的n皇后摆放问题。
八皇后问题的第一个解是在1850年由弗朗兹·诺克给出的。
诺克也是首先将问题推广到更一般的n皇后摆放问题的人之一。
1874年,S.冈德尔提出了一个通过行列式来求解的方法,这个方法后来又被J.W.L.格莱舍加以改进。
转化规则:其实八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。
当且仅当n = 1 或n ≥ 4 时问题有解。
令一个一位数组a[n]保存所得解,其中a[i] 表示把第i个皇后放在第i行的列数(注意i的值都是从0开始计算的),下面就八皇后问题做一个简单的从规则到问题提取过程。
(1)因为所有的皇后都不能放在同一列,因此数组的不能存在相同的两个值。
(2)所有的皇后都不能在对角线上,那么该如何检测两个皇后是否在同一个对角线上?我们将棋盘的方格成一个二维数组,如下:
假设有两个皇后被放置在(i,j)和(k,l)的位置上,明显,当且仅当
|i-k|=|j-l|时,两个皇后才在同一条对角线上。
算法原型:上面我们搞清楚了在解决八皇后问题之前需要处理的两个规则,并将规则转化到了我们数学模型上的问题,那么这段我们开始着手讨论如何设计
明显,回溯的思想是:假设某一行为当前状态,不断检查该行所有的位置是否能放一个皇后,检索的状态有两种:
(1)先从首位开始检查,如果不能放置,接着检查该行第二个位置,依次检查下去,直到在该行找到一个可以放置一个皇后的地方,然后保存当前状态,转到下一行重复上述方法的检索。
(2)如果检查了该行所有的位置均不能放置一个皇后,说明上一行皇后放置的位置无法让所有的皇后找到自己合适的位置,因此就要回溯到上一行,重新检查该皇后位置后面的位置。
是否注意到?如果我们用一个数组来保存当前的状态,上面的检索过程是否有点像堆栈的操作?如果找到可行位置,压栈,如果当前行所有位置不行,将出栈。
好了,问题模型逐渐清晰开来了,我们可以定义一个过程,这个过程负责检索的过程,如果检索到当前行某个位置可行,压栈,如果当前行所有位置不行,将执行出栈操作。
8皇后问题,我们假定栈的大小为8,如果栈满了,表示找到了可行方法,将执行所有出栈操作。
也许有同学会问:如果我找到了一个方法,在进入找下一个可行方法时,该如何做到找出的方法不重复?我们是否需要为每行设定一个状态变量?其实这个问题的处理方法很简单:其实我们在回溯的时候,每个皇后所在位置就是该行的状态变量,回溯转到下一个位置的时候,只需后移1位即可,也就是i++。
OK,其实我们可以使用一个数组来模拟栈的结构就可以了,上面解说的时候不用数组而使用栈是因为栈的结构比数组更形象而已。
根据上述想法,我们必须定义一个过程,这个过程用来检查当前行的某个位置是否可行,为了方便大家阅读,我采用了常用的算法描述语言SPARKS 。
SPARKS 有个最大的特点就是非常注重算法的思想而不是代码,这样可以更加清晰明了地帮助读者了解作者的算法思想。
(2)利用上述的检索过程,通过递归的方式,来确定每个皇后的位置———回。