用回溯法解決八皇后問題的思路,並求出17皇后解的數量(c#,c++,python表示)
1.解決思路
借用網上一張圖,中間那個紅點表示的就是皇后,這圖的意思也就是一個皇后的影響範圍為這這個皇后所在的那一列,那一行,對角線以及次對角線。其它的皇后不能在它的影響範圍裡否則會衝突。八皇后也就是在一個8x8的棋盤上後置8個皇后,皇后的數量與棋盤的行列數一樣。這是基礎,再來說說回溯法,回溯法最重要的思想就是當前這一步走不通,我們就掉頭返回上一步找其他方案。這相對於列舉法列出所有可能再逐個進行排除要節省資源的多。
首先皇后的衝突規則是在同一行,同一列,同一對角線,同一次對角線。因為多個皇后不可能在同一行,所以我們可以看成第一個皇后在第一行,第二個皇后在第二行.........再用一個值來表示陣列來表示皇后是處於第幾列的,那麼某個皇后在棋盤上的位置就可以用這是第幾個皇后表示y,這個皇后對應數組裡的值表示x。並且這樣對於檢測衝突可以少對比一次,只需要檢測時候是否處於同一列,同一對角線,同一次對角線即可。
再來說說回溯法在n皇后問題上的應用,在棋盤上我們可以先放第一個皇后,再放一個皇后再檢驗衝突,如果遇到衝突將這個皇后的位置向右移動,如果這個皇后移動超出了棋盤,這表明這種情況下沒有解,對此可以把這個皇后拿出棋盤並移動上一個皇后,直到上一個皇后也不衝突,在繼續放置下一個皇后,找出不衝突的位置,如果到了就這樣放到了第八個皇后仍不衝突那麼得到一種解。在得到了一種解後再把第8個皇后拿出棋盤,移動第7個皇后,繼續找出不衝突的位置,如果第7個皇后剩下的位置全衝突,那把第7個皇后拿出棋盤,再移動第6個皇后的位置........就醬,如果第一個皇后移動超出了棋盤那麼表示已經找出所有解。
2.程式碼
using System; namespace n皇后問題 { class Program { static int Queen_n =12;//皇后數量 static int[] queens = new int[Queen_n];//儲存所有皇后的位置 static int QueenSolution_n = 0;//有多少解 static double time = 0; static void Main(string[] args) { time = Environment.TickCount;//記錄程式啟動時間 for (int i = 0; i < Queen_n; i++)//對所有皇后初始化 { queens[i] = 0; } nQueen(0); Console.WriteLine(Queen_n + "皇后問題有" + QueenSolution_n + "種解,用時"+(Environment.TickCount-time)+"ms"); Console.ReadKey(); } static bool IS = false; static void nQueen(int now) { for (int i = now; i < Queen_n; i++) { while (IsConlict(i) || queens[i] >= Queen_n)//如果當前皇后的位置與前幾個衝突,或者超出棋盤,進來返回上一層 { queens[i]++;//皇后位置右移 if (queens[i] >= Queen_n)//如果這皇后沒有結果 { if (queens[0] >= Queen_n)//第一個皇后越界,結束 { return; } queens[i] = 0;//當前皇后位置歸0 i--;//返回上一個皇后 queens[i]++;//當前皇后位置右移 nQueen(i);// return ;//釋放 } } if (i == Queen_n - 1)//如果到了第八個皇后,且不衝突。得到一種解 { //Console.WriteLine("解+1,目前是第"+QueenSolution_n); queens[i] = 0;//當前皇后位置歸0 i--;//返回上一個皇后 queens[i]++;//當前皇后位置右移 QueenSolution_n++;//解+1 nQueen(i); return; } } } static bool IsConlict(int now)//當前是第now個皇后,一個皇后佔一行,所以它是第幾個皇后也就表示,這個皇后在第幾行 { for (int i = 0; i < now; i++) {//在同一對角線,在同一斜對角線,在同一列上 if (((i - now) == (queens[i] - queens[now])) || ((i - now) == -(queens[i] - queens[now])) || (queens[i] == queens[now])) { return true;//衝突 } } return false; } } }
但是
這是由於遞迴太多導致的棧溢位,對此可以改變思路不是直接對函式遞迴而是迭代。
修改後的程式碼
using System; namespace n皇后問題 { class Program { static int Queen_n =12;//皇后數量 static int[] queens = new int[Queen_n];//儲存所有皇后的位置 static int QueenSolution_n = 0;//有多少解 static double time = 0; static void Main(string[] args) { time = Environment.TickCount; for (int i = 0; i < Queen_n; i++)//對所有皇后初始化 { queens[i] = 0; } nQueen(0); Console.WriteLine(Queen_n + "皇后問題有" + QueenSolution_n + "種解,用時"+(Environment.TickCount-time)+"ms"); Console.ReadKey(); } static bool IS = false; static void nQueen(int now) { for (int i = now; i < Queen_n; i++) { while (IsConlict(i) || queens[i] >= Queen_n)//如果當前皇后的位置與前幾個衝突,或者超出棋盤,進來返回上一層 { queens[i]++;//皇后位置右移 if (queens[i] >= Queen_n)//如果這皇后沒有結果 { if (queens[0] >= Queen_n)//第一個皇后越界,結束 { return; } queens[i] = 0;//當前皇后位置歸0 i--;//返回上一個皇后 queens[i]++;//當前皇后位置右移 IS = true; } } if (IS==true)//因為在for後會在++,所以如果是返回上一層需要再減一次 { i--; IS = false; } if (i == Queen_n - 1)//如果到了第八個皇后,且不衝突。得到一種解 { queens[i] = 0;//當前皇后位置歸0 i--;//返回上一個皇后 queens[i]++;//當前皇后位置右移 QueenSolution_n++;//解+1 i--; } } } static bool IsConlict(int now)//當前是第now個皇后,一個皇后佔一行,所以它是第幾個皇后也就表示,這個皇后在第幾行 { for (int i = 0; i < now; i++) {//在同一對角線,在同一斜對角線,在同一列上 if (((i - now) == (queens[i] - queens[now])) || ((i - now) == -(queens[i] - queens[now])) || (queens[i] == queens[now])) { return true;//衝突 } } return false; } } }
我這種弱雞畢竟不是ACM選手,也就只能這種程度了。
C++
// n皇后問題c++表示.cpp: 定義控制檯應用程式的入口點。
//
#include "stdafx.h"
#include <iostream>
#include <time.h>
using namespace std;
static int Queen_n = 12;//皇后數量
static int queens[20] = {0};//儲存所有皇后的位置
static int QueenSolution_n = 0;//有多少解
bool IsConlict(int now)//當前是第now個皇后,一個皇后佔一行,所以它是第幾個皇后也就表示,這個皇后在第幾行
{
for (int i = 0; i < now; i++)
{//在同一對角線,在同一斜對角線,在同一列上
if (((i - now) == (queens[i] - queens[now])) || ((i - now) == -(queens[i] - queens[now])) || (queens[i] == queens[now]))
{
return true;//衝突
}
}
return false;
}
void nQueen()
{
for (int i = 0; i < Queen_n; i++)
{
while (IsConlict(i) || queens[i] >= Queen_n)//如果當前皇后的位置與前幾個衝突,或者超出棋盤,進來返回上一層
{
queens[i]++;//皇后位置右移
if (queens[i] >= Queen_n)//如果這皇后沒有結果
{
if (queens[0] >= Queen_n)//第一個皇后越界,結束
{
return;
}
queens[i] = 0;//當前皇后位置歸0
i--;//返回上一個皇后
queens[i]++;//當前皇后位置右移
i--;//因為在for後會在++,所以如果是返回上一層需要再減一次
}
}
if (i == Queen_n - 1)//如果到了第八個皇后,且不衝突。得到一種解
{
queens[i] = 0;//當前皇后位置歸0
i--;//返回上一個皇后
queens[i]++;//當前皇后位置右移
QueenSolution_n++;//解+1
i--;
}
}
}
int main()
{
double time = clock();
nQueen();
time =clock() - time;
cout << Queen_n << "皇后問題有" << QueenSolution_n << "種解,用時" <<time<<"ms"<< endl;
cin.get();
return 0;
}
python
import time
Queen_n = 8#皇后數量
queens = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]#儲存所有皇后的位置
QueenSolution_n = 0#有多少解
def IsConlict(now):#當前是第now個皇后,一個皇后佔一行,所以它是第幾個皇后也就表示,這個皇后在第幾行
for x in range(0,now):#在同一對角線,在同一斜對角線,在同一列上
if (((x - now) == (queens[x] - queens[now])) or ((x - now) == -(queens[x] - queens[now])) or (queens[x] == queens[now])):
return True#衝突
return False
def nQueens():
i=0
while i<Queen_n:
while (IsConlict(i) or queens[i] >= Queen_n):
queens[i]+=1
if queens[i]>=Queen_n:
if queens[0]>=Queen_n:#越界結束
return
queens[i]=0
i-=1
queens[i]+=1
i-=1
if i==Queen_n-1:
queens[i]=0
i-=1
queens[i]+=1
global QueenSolution_n
QueenSolution_n+=1
i-=1
i+=1
time0=time.clock()
nQueens()
print(Queen_n, "皇后問題有",QueenSolution_n,"種解,用時" ,int((time.clock()-time0)*1000),"ms")