回溯法·递归 - 八皇后问题 Eight queens puzzle

问题描述:

在8X8格的国际象棋棋盘上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

 

问题引申:

在n*n格的国际象棋棋盘上摆放n个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

 

算法思路:

实现一个逐行逐行地放置皇后的函数

在第X行中的一格放上一个皇后

判断这个皇后和前几行的皇后等否互相攻击,如果能则拿起来重新放到下一列,直到不会和前几行的皇后相互攻击后,再转到X+1行继续放皇后

然后递归调用该函数来解该问题。

 

最初我考虑的是建立一个n*n的数组表示一个棋盘,每个元素代表一个方格,通过不同的值来表示格子有没有放皇后和会不会被其他皇后攻击。

后来发现其实完全没有必要那么复杂_(:з」∠)_。只需要用一维数组就能表示所有皇后的的位置:

int Queen[MAX];

例如Queen[index],index的值表示行数,Queen[index]的值表示列数,那么Queen[3] = 4就表示第三行、第四列有一个皇后。那么通过判断Queen[i]和Queen[j]的值是否相等就能知道i,j两个皇后是不是在同一列了。

 

那么又该怎么判断i和j两个皇后是不是在同一斜线上呢?

我想到了中学学到的一元一次函数。

y = a * x + b

a是斜率,b是截距。很容易就知道棋盘中的斜线的斜率全都是1或-1,那么判断两个皇后是不是在同一斜线上,只需要判断b是否相等就行了。

斜率为1时 b = y - x,为-1时, b = y + x 。我用两个数组分别记录1和-1的情况:

int PlusB[MAX];
int MinusB[MAX];

Queen[index] = i;
PlusB[index] = index - i;
MinusB[index] = index + i;

那么很容易地,通过判断PlusB[i]和PlusB[j],MinusB[i]和MinusB[j]是否相等就能知道i,j两个皇后是不是在同一条斜线上了。

 

如此,就能得到判断一个皇后会不会和前几行的皇后相互攻击的函数了:

 

bool Attack( int index )
{
    for (int i = 0; i < index; i++)
    if (Queen[i] == Queen[index] || PlusB[i] == PlusB[index] || MinusB[i] == MinusB[index])
        return true;
    return false;
}

 

 

那么递归调用的放置皇后的函数也很轻松地就实现了:

void PutQueen( int index )
{
    if (index == n)
    {
        Result++;
        return;
    }
    for (int i = 0; i < n; i++)
    {
        Queen[index] = i;
        PlusB[index] = index - i;
        MinusB[index] = index + i;
        if (!Attack( index )) PutQueen( index + 1 );
    }
}

通过index表示行数,即在第index行上放置皇后,每次判断皇后会否互相攻击,若不会,则index+1后递归调用函数,如果会攻击则换一列放置皇后。直到index == n,则给结果加一并回溯一步换一列放置皇后,或者试完第index行的所有列了,又会回溯到上一步。

 

整个程序的代码如下:

 1 #include <fstream>
 2 
 3 using namespace std;
 4 
 5 int n = 0;            // Number of Queens
 6 int Result = 0;
 7 const int MAX = 100;
 8 int Queen[MAX];
 9 int PlusB[MAX];
10 int MinusB[MAX];
11 FILE *fp;
12 
13 void OutPut( );
14 bool Attack( int index );
15 void PutQueen( int index );
16 
17 int main()
18 {
19     printf( "Input the number of queens: " );
20     scanf( "%d", &n );
21     fp = fopen( "Result.txt", "w" );
22     int index = 0;
23     if (n >= 4 && n <= 100)
24         PutQueen( index );
25     else
26         printf( "Out of Range!\n" );
27     fclose( fp );
28     printf( "The %d queens puzzle has %d distinct solutions.\n", n, Result );
29     return 0;
30 } 31 32 void OutPut()    // Output to files 33 { 34 for (int i = 0; i < n; i++) 35 { 36 for (int j = 0; j < Queen[i]; j++) 37 fprintf( fp, " %d", 0 ); 38 fprintf( fp, " %d", 1 ); 39 for (int k = Queen[i] + 1; k < n; k++) 40 fprintf( fp, " %d", 0 ); 41 fprintf( fp, "\n" ); 42 } 43 fprintf( fp, "\n" ); 44 } 45 46 bool Attack( int index ) 47 { 48 for (int i = 0; i < index; i++) 49 if (Queen[i] == Queen[index] || PlusB[i] == PlusB[index] || MinusB[i] == MinusB[index]) 50 return true; 51 return false; 52 } 53 54 void PutQueen( int index ) 55 { 56 if (index == n) 57 { 58 Result++; 59 OutPut(); 60 return; 61 } 62 for (int i = 0; i < n; i++) 63 { 64 Queen[index] = i; 65 PlusB[index] = index - i; 66 MinusB[index] = index + i; 67 if (!Attack( index )) PutQueen( index + 1 ); 68 } 69 }

 

 

posted @ 2013-12-21 23:10  活在二次元的伪触  阅读(1838)  评论(12编辑  收藏  举报