八皇后問題(回溯法&枚舉法)

作者 : 卿篤軍


本文討論了八皇后問題的三種解決方案:

一、枚舉法

二、回溯法(遞歸版)

三、回溯法(非遞歸版)

本來這些代碼是以前編寫好的,沒有發表,由於最近又學習到了八皇后問題,自己整理了一下發表了出來!


首先、說明一下何爲八皇后問題,我也不去谷歌了,直接簡單的說明一下:

八皇后問題,就是在一個8*8的平面棋盤上,要求你擺放8個棋子,要求:這8個棋子不能有2個在同一行,也不能有2個在同一列,同時一條斜線上面也不能有2個~~~~

比如:4*4的棋盤,你可以這樣擺放(4皇后問題):

\

以上圖爲參照,我們分析一下,要使棋子不衝突,那算法要如何寫?<喎�"/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+ztLDx72rtv7OrMr91+m9tc6q0rvOrMr91+mjrLKi08PPwsPm1eLW1re9yr3AtLHqvMfG5dfTo7o8L3A+CjxwPsq+wP2juiBhWzFdID0gMiCjrLHtyr612jHB0LXaMtDQ09DG7NfToaM8L3A+CjxwPmFbMl0gPSA0IKOsILHtyr612jLB0LXaNNDQ09DG7NfToaM8L3A+CjxwPmFbM10gPSAxIKOsILHtyr612jPB0LXaMdDQ09DG7NfToaM8L3A+CjxwPmFbNF0gPSAzIKOsse3KvrXaNMHQtdoz0NDT0Mbs19OhozwvcD4KPHA+PGJyPgo8L3A+CjxwPrrDo6y909fFt9bO9rPlzbvL47eouMPI57rOseDQtKO6o6iz5c27y+O3qKO6vs3Kx9PDwLTF0LbPwuTG5dfTzrvWw8rHt/HT68bky/zG5dfTs+XNu6OpPC9wPgo8cD6/ycTcs/bP1rXEvLjW1rPlzbvH6b/2o7o8L3A+CjxwPjGjqbXa0rvQ0NPQwb249sbl19OjrNOmuMPKx9Xi0fmx7cq+o7phWzFdID0gMSwgYVsyXSA9IDEsse3KvrXa0rvB0LrNtdq2/sHQtcS12tK70NC2vNPQxuXX06Oo1N3H0rK7v7zCxzMsNMHQo6mhozwvcD4KPHA+MqOp1ve21L3Hz9/Jz9PQMrj2xuXX06Os06a4w8rH1eLR+bHtyr6jumFbMV0gPSAyLCBhWzJdID0gMyyx7cq+tdrSu8HQtdq2/tDQo6y12rb+wdC12sj90NDOu9bDyc/T0Mbl19OhozwvcD4KPHA+M6OptM621L3Hz9/Jz9PQMrj2xuXX06Os06a4w8rH1eLR+bHtyr6jumFbM10gPSA0LCBhWzRdID0gMyyx7cq+tdrI/cHQtdrLxNDQo6y12svEwdC12sj90NDOu9bDyc/T0Mbl19OhozwvcD4KPHA+PHN0cm9uZz7XotLio7qyu7/JxNzU2sHQyc+z9s/Ws+XNu6Os0vLOqqOsztLDx8rHsLTB0LDat8W1xKGjvLSjurXa0rvB0MnPsNq3xdK7uPajrMi7uvPU2rXatv7B0MnPsNq3xS4uLi4utdrI/cHQo6zDv8HQsNq3xdK7uPbG5dfToaM8L3N0cm9uZz48L3A+CjxwPs/Cw+ax4NC0tPrC66O6o6g8c3Ryb25nPse/tfejus/Cw+az9s/WtcSyu7ncysdpu7nKx2qx7cq+tcS2vMrHwdCjurXaacHQo6y12mrB0Dwvc3Ryb25nPqOpPC9wPgo8cD48c3Ryb25nPtDQs+XNu6O6PC9zdHJvbmc+PC9wPgo8cD7K18/Io6y87LLitdrSu9DQtcQxLTjB0KOsyse38bPlzbuho8i7uvO87LLitdq2/tDQLi4uLi4uLi4uLi61sci7xuTKtbXa0rvB0LK70OjSqrzssuKjqNLyzqqw2rfFtdrSu7j2xuXX07XEyrG68qOsxuXFzMnPu7nDu9PQxuXX06Ossru/ycTcs+XNu6OpPC9wPgo8cD48L3A+CjxwcmUgY2xhc3M9"brush:java;">for (int i = 1; i <= 8; ++i) //檢測1~8行{for (int j = 1; j <= 8; ++j) //檢測1~8列{if (a[i] == a[j])return "衝突";}}return "不衝突";對角線衝突:

這裏稍微用數學分析一下,用i,j表示當前正在檢測的兩列(i外層for循環,j內層for循環),那麼a[i] ,a[j] 的值就分別表示當前檢測列棋子擺放的位置即行(每列只有1個棋子)。

如果兩個棋子對角線衝突(正反對角線衝突),則必然有:

\

轉化爲代碼:

1
2
3
4
5
6
7
8
9
for (int i = 1; i <= 8; ++i)  //檢測1~8行
{
    for (int j = 1; j <= 8; ++j) //檢測1~8列
    {
        if ((a[i] - a[j] == i - j)  || (a[i] - a[j] == j - i))  //對角線衝突
            return "衝突";
    }
}
return "不衝突";

優化整理後的衝突判斷代碼就出爐了,如下所示:

1
2
3
4
5
6
7
8
9
//位置衝突算法
bool Chongtu(int a[], int n)//a[]位置數組,n皇后個數
{
    for (int i = 2; i <= n; ++i)//i:位置
        for (int j = 1; j <= i-1; ++j)//j:位置
            if ((a[i] == a[j]) || (abs(a[i]-a[j]) == i-j))//1:在一行;2:在對角線上
                return false;   //衝突
    return true;//不衝突
}
關於內層for循環j<=i-1,因爲判斷第i個棋子是否衝突(擺放是否合理),我們只需要和前面i-1列校對就ok了。這樣也保證了,i>j的恆成立。所以對角線衝突了簡化了一下。

好了,該說明的都說明了,現在編寫第一個八皇后代碼~~~~


枚舉法:

思想:八重枚舉,枚舉出所以擺放的情況(不管合理不合理),然後到第八層for裏面判斷當前枚舉出來的情況是否合理~~~~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <stdio.h>
#include <math.h>
//位置衝突算法
bool Chongtu(int a[], int n)//a[]位置數組,n皇后個數
{
    int i = 0, j = 0;
 
    for (i = 2; i <= n; ++i)//i:位置
        for (j = 1; j <= i-1; ++j)//j:位置
            if ((a[i] == a[j]) || (abs(a[i]-a[j]) == i-j))//1:在一行;2:在對角線上
                return false;   //衝突
    return true;//不衝突
}
//八皇后:枚舉算法
void Queens8()
{
    int a[9] = {0}; //用於記錄皇后位置:(第0行0列我們不用)。如:a[3] = 4;表示第3列第4行位置有皇后
    int i = 0,count = 0//用於計數
 
    for (a[1] = 1; a[1] <= 8; ++a[1])
        for (a[2] = 1; a[2] <= 8; ++a[2])
            for (a[3] = 1; a[3] <= 8; ++a[3])
                for (a[4] = 1; a[4] <= 8; ++a[4])
                    for (a[5] = 1; a[5] <= 8; ++a[5])
                        for (a[6] = 1; a[6] <= 8; ++a[6])
                            for (a[7] = 1; a[7] <= 8; ++a[7])
                                for (a[8] = 1; a[8] <= 8; ++a[8])
                                {
                                    if (!Chongtu(a,8))//如果衝突,則繼續枚舉
                                        continue;
                                    else
                                    {
                                        printf("第%d情況:",++count);
                                        for (i = 1; i <= 8; ++i)
                                            printf("%d ",a[i]);//打印某種情況
                                        printf("\n");
                                    }
                                }
}
//主函數
int main()
{
    Queens8();
 
    return 0;
}</math.h></stdio.h>

回溯法(遞歸版):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
#include <math.h>
 
int a[9] = {0};
int n = 8, count = 0;
 
//位置衝突算法
bool Chongtu(int a[], int n)//a[]位置數組,n皇后個數
{
    int i = 0, j = 0;
 
    for (i = 2; i <= n; ++i)//i:位置
        for (j = 1; j <= i-1; ++j)//j:位置
            if ((a[i] == a[j]) || (abs(a[i]-a[j]) == i-j))//1:在一行;2:在對角線上
                return false;   //衝突
    return true;//不衝突
}
 
//八皇后問題:回溯算法(遞歸版)
void Queens8(int k) //參數k:遞歸擺放第k個皇后
{
    int i = 0;
 
    if (k > n)      //k>n:即k>8表示最後一個皇后擺放完畢
    {
        printf("第%d種情況:",++count);
        for (i = 1; i <= n; ++i)
            printf("%d ",a[i]);//打印情況
        printf("\n");
    }
    else   //8個皇后未全部擺放完畢       
    {
        for (i = 1; i <= n; ++i)//擺放第k個皇后時(轉下一行)
        {       //依次從列頂端開始搜索,一直到列底端,直到找到合適位置,如果未找到,自動返回上層遞歸(回溯)
            a[k] = i;              
            if (Chongtu(a,k))//不衝突
                Queens8(k+1);//遞歸擺放下一個皇后
        }
    }
    return;
}
 
//主函數
int main()
{
    Queens8(1);//參數1:表示擺放第1個皇后
 
    return 0;
}
</math.h></stdio.h>


回溯法(非遞歸版):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <stdio.h>
#include <math.h>
 
//位置衝突算法
bool Chongtu(int a[], int n)//a[]位置數組,n皇后個數
{
    int i = 0, j = 0;
 
    for (i = 2; i <= n; ++i)//i:位置
        for (j = 1; j <= i-1; ++j)//j:位置
            if ((a[i] == a[j]) || (abs(a[i]-a[j]) == i-j))//1:在一行;2:在對角線上
                return false;   //衝突
    return true;//不衝突
}
 
//八皇后問題:回溯法(非遞歸)
void Queens8()
{
    int n = 8;        //8個皇后
    int count = 0;    //記錄當前第幾情況
    int a[9] = {0};   //存放皇后位置,如:a[2] = 4;表示第2列第4行有一個皇后(a[0]不用)
    int i = 0,k = 1//初始化k爲第一列
 
    a[1] = 0;         //初始化a[1] = 0
     
    while (k > 0)     //k==0時:表示擺放第1個皇后就超過了列底部(即已經找完所有情況)
    {
        a[k] += 1;    //a[k]位置,擺放一個皇后
        while ((a[k] <= n) && (!Chongtu(a,k)))//如果a[k](即皇后擺放位置)沒有到列最底部,且擺放衝突。
            a[k] += 1;//將皇后列下移一位
        if (a[k] <= n)//皇后擺放位置沒有到達列最底部
        {
            if (k == n)//k==n表示,8列皇后全部擺放完畢
            {
                printf("第%d種情況:",++count);
                for (i = 1; i <= n; ++i)//打印情況
                    printf("%d ",a[i]);
                printf("\n");
            }
            else      //皇后還未擺放完畢
            {
                k += 1;    //繼續擺放下一列
                a[k] = 0//此行初始化a[k] = 0;表示第k列,從第一行開始擺放皇后
            }
        }
        else  //回溯:當a[k]>8進入else,表示在第k列中沒有找到合適的擺放位置
            k -= 1;//回溯到k-1步:k表示第幾個皇后,a[k]表示第k個皇后擺放的位置
    }
    return;
}
 
//主函數
int main()
{
    Queens8();
 
    return 0;
}</math.h></stdio.h>

發佈了39 篇原創文章 · 獲贊 71 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章