回溯法解八皇后问题及再看八皇后问题优化
2015-01-11 09:52
507 查看
1、介绍
先上张图来说明用回溯法解八皇后问题的每一步:![](http://my.csdn.net/uploads/201205/06/1336311585_2632.gif)
![](http://my.csdn.net/uploads/201205/06/1336314020_3122.png)
2、程序
看着严蔚敏的书写的,写好后运行一次性成功了,程序如下:// N皇后问题 #include <iostream> using namespace std; #define N 8 bool matrix[N + 1][N + 1] = {0}; bool IsLegal(bool matrix[N + 1][N + 1], const int &i, const int &j) { // 判断前面的i-1个棋子与matrix[i][j]是否冲突,i为1时合法 for (int m = 1; m <= i - 1; ++m) { for (int n = 1; n <= N; ++n) { // 实际每一行只有一个棋子 if (matrix[m] == 1) { if ( n == j || abs(i - m) == abs(j - n) ) // key, not bad return false; } } } return true; } void Print(bool matrix[N + 1][N + 1]) { static int count = 1; printf("Case %d:\n", count++); for (int i = 1; i <= N; i++) { for (int j = 1; j <= N; j++) { matrix[i][j] == 1 ? printf("%c ", 2) : printf(". "); } cout << endl; } cout << endl; } void Trial(const int i) { // 进入本函数时,在N*N的棋盘前i-1行已放置了互不攻击的i-1个棋子 // 现从第i行起继续为后续棋子选择合适位置 if (i > N) // 输出当前的合法布局 Print(matrix); else for (int j = 1; j <= N; ++j) { matrix[i][j] = 1; if ( IsLegal(matrix, i, j) ) Trial(i + 1); matrix[i][j] = 0; } } int main(void) { Trial(1); return 0; }运行结果:
![](http://my.csdn.net/uploads/201205/06/1336312447_2530.png)
3、数学问题
关于n皇后的解的个数(8皇后是92个解):[cpp] view
plaincopyprint?
![](https://code.csdn.net/assets/CODE_ico.png)
n a(n)
1 1
2 0
3 0
4 2
5 10
6 4
7 40
8 92
9 352
10 724
11 2680
12 14200
13 73712
14 365596
15 2279184
16 14772512
17 95815104
18 666090624
19 4968057848
20 39029188884
21 314666222712
22 2691008701644
23 24233937684440
24 227514171973736
25 2207893435808352
26 22317699616364044
独立解的问题我就不多提了。目前这个数列还没找到通项公式。有意思的是,高斯算八皇后的解的个数时,他算错了,他的答案是76种,不知道他漏了哪种,呵呵。(不过也是4的倍数)
4、想法
那个Trial递归函数我还没弄明白,对着书抄的,要是自己想,难。还有待研究推广。把判断是否合法的IsLegal函数优化了,原来的程序是O(N^3),现在是 O(N^2):
// N皇后问题 #include <iostream> using namespace std; #define N 8 int matrix[N + 1][N + 1] = {0}; // matrix[0][j]为空,matrix[i][0]中放第i行的皇后的列坐标(从1开始记) bool IsLegal(const int &i, const int &j) { // 判断前面的i-1个棋子(坐标是matrix[m] )与matrix[i][j]是否冲突,i为1时合法 for (int m = 1; m <= i - 1; ++m) { int n = matrix[m][0]; if ( n == j || abs(i - m) == abs(j - n) ) return false; } return true; } void Print(void) { static int count = 1; printf("Case %d:\n", count++); for (int i = 1; i <= N; i++) { for (int j = 1; j <= N; j++) { matrix[i][j] == 1 ? printf("%c ", 2) : printf(". "); } cout << endl; } cout << endl; } void Trial(const int &i) { // 进入本函数时,在N*N的棋盘前i-1行已放置了互不攻击的i-1个棋子 // 现从第i行起继续为后续棋子选择合适位置 if (i > N) // 输出当前的合法布局 Print(); else for (int j = 1; j <= N; ++j) { matrix[i][j] = 1; if ( IsLegal(i, j) ) { matrix[i][0] = j; Trial(i + 1); } matrix[i][j] = 0; } } int main(void) { Trial(1); return 0; }
再看八皇后问题
1、问题描述
在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。2、解法一
可以用一个一维数组来表示棋盘,seat[i]=j;表示第i行皇后放在第j列位置上。其中j有八种情况,i依次递增,直到增到8时说明每一行都放了一个皇后,这时我们就可以判断棋盘上的皇后是否冲突,如果不冲突则找到一种情况。当然为了提高效率,在每一行第j列放入一个皇后时可以先判断这次放入是否和前面几行已经存在的皇后冲突,如果冲突则把皇后放在下一列,这就是回溯法中的剪枝,可以用一个剪枝函数来实现。参考代码:
[cpp] view
plaincopy
/******************************************************************************
1. 八皇后问题要求在一个8*8的棋盘上放上8个皇后,使得每一个皇后既攻击不到另外
七个皇后,也不被另外七个皇后所攻击.按照国际象棋的规则,一个皇后可以攻击与之处在
同一行或同一列或同一斜线上的其他任何棋子.因此,八皇后问题等于要求八个皇后中的任
意两个不能被放在同一行或同一列或同一斜线上。
*******************************************************************************/
#include<stdio.h>
# define QUEEN 8
int seat[QUEEN];//皇后的位置,按行放置
int count;//记录总共有多少种放法
void queen(int n);//放置皇后
int ok(int n);//判断放置皇后后是否与其他的n-1个皇后冲突
void main()
{
count=0;
queen(0);//从第0行开始放
printf("总共有%d种放法\n",count);
}
//放置皇后
void queen(int n)
{
int i;
if(n==QUEEN){
count++;
return ;
}
for(i=0;i<QUEEN;i++){
seat
=i;//放置第n行的皇后,把他放在i列上
if(ok(n)){//如果放置没有问题,放置下一个皇后
queen(n+1);
}
}
//return;
}
// 剪枝函数,判断放置皇后后是否与其他的n-1个皇后冲突
int ok(int n)
{
int i;
for(i=0;i<n;i++){
if((seat[i]-i)==(seat
-n)||(seat[i]+i)==(seat
+n)||(seat[i]==seat
))
return 0;
}
return 1;
}
3、解法二
棋盘可以看出是0和1组成的,0表示没有放置皇后,1表示放置皇后00000001
00000010
00000100
00001000
00010000
00100000
01000000
10000000
给每一行放置一个皇后,不外乎就这八种情况,其中每一种情况对应一个字节,可以用个数为8的char数组来表示这八种情况,然后对这些情况进行全排列,每一种排列判断有没有对角线上的皇后发生冲突,没有冲突则找到一种情况。这里也可以设计一个剪枝函数来提高效率。给出的代码中并没有设计这个剪枝函数。
参考代码:
[cpp] view
plaincopy
#include<stdio.h>
int count=0;//记录有多少种摆法
//字符数组
char seat[]={1,1<<1,1<<2,1<<3,1<<4,1<<5,1<<6,1<<7};
//获得二进制位1在unsigned char中的第几位
int get(unsigned char ch)
{
int i;
for(i=0;i<8;i++)
if(1<<i==ch)
return i;
}
//判断放置皇后后是否与其他的n-1个皇后冲突
int ok()
{
int i,j;
for(i=0;i<8;i++){
for(j=i+1;j<8;j++){//当行列之和或之差相等时说明这两个位置是对角线关系
if((get(seat[i])-i)==(get(seat[j])-j)||(get(seat[i])+i)==(get(seat[j])+j))
return 0;
}
}
return 1;
}
//序数法(排出的序列为有序的)
//str:待排序的字符序列 n:首字符下标 len:字符串长度
void ordinal(unsigned char * str,int n,int len)
{
int i;
unsigned char temp;
if(n==len){
if(ok())
count++;
return;
}
for(i=n;i<len;i++){
temp=str[i];
str[i]=str
;
str
=temp;
ordinal(str,n+1,len);
temp=str[i];
str[i]=str
;
str
=temp;
}
}
void main()
{
ordinal(seat,0,8);
printf("%d\n",count);
}
4、运行结果
总共有92种放法相关文章推荐
- 八皇后问题回溯算法演示系统
- 八皇后问题回溯算法演示系统
- C#WPF实现回溯算法解决八皇后问题
- 八皇后问题、N皇后问题回溯法详解
- 八皇后问题 --回溯
- 从经典的八皇后问题看回溯
- Queen 八皇后问题 回溯经典解法
- 回溯法-八皇后问题之C实现
- 回溯经典问题之八皇后问题
- uva 11553 Grid Game (回溯- 类似,比八皇后问题简单)
- 回溯法解八皇后问题
- 回溯法:最大装载问题(使用递归,优化搜索的同时取得最佳路径)
- 回溯法:最大装载问题(使用递归,优化无最优解时候的右子树搜索)
- 八皇后问题的回溯算法
- 【算法复习二】八皇后问题 ---- 回溯
- 八皇后问题 回溯递归 C语言版
- 回溯法:最大装载问题(使用递归,不做任何优化)
- 八皇后问题 C#版本算法 回溯法
- 八皇后问题 DFS,回溯剪枝 //http://poj.grids.cn/practice/2698
- uva 11553 Grid Game (回溯- 类似,比八皇后问题简单)