八皇后之回溯法解決
問題描述:
要在8*8的國際象棋棋盤中放8個皇后,使任意兩個皇后都不能互相吃掉。規則是皇后能吃掉同一行、同一列、同一對角線的棋子。如下圖即是兩種方案:
、
解決方案:
8*8的棋盤要擺放8個皇后,且不能同行同列同對角線,那麼每行必定會有一個皇后。我們可以設一個陣列a用來存放每一行皇后的位置,元素值表示第幾列(如a[1]=5表示第一行的皇后處於第五個格)。然後只需要求出陣列a的值 問題就解決了,下面介紹三種回溯解法:
1、八個for迴圈。用列舉的辦法,八個for迴圈分別列舉每一行的8個位置,但是我們不用全部列舉完,可以採用“剪枝策略”,即遇到不適合的情況就回溯。例如當a[1]=4,第二行a[2]=4與a[1]同列,不符合題意。接下來的六個迴圈就不用窮舉下去了,直接"continue;"去檢驗a[2]=5.....具體程式碼如下:
void main()
{
int a[9];
int i,t=1;
for(a[1]=1;a[1]<9;a[1]++)
for(a[2]=1;a[2]<9;a[2]++)
{
if(!Check(a,2)) continue;
for(a[3]=1;a[3]<9;a[3]++)
{
if(!Check(a,3)) continue;
for(a[4]=1;a[4]<9;a[4]++)
{
if(!Check(a,4)) continue;
for(a[5]=1;a[5]<9;a[5]++)
{
if(!Check(a,5)) continue;
for(a[6]=1;a[6]<9;a[6]++)
{
if(!Check(a,6)) continue;
for(a[7]=1;a[7]<9;a[7]++)
{
if(!Check(a,7)) continue;
for(a[8]=1;a[8]<9;a[8]++)
{
if(!Check(a,8)) continue;
else
{
printf("第%d種解法:\n",t++);
for(i=1;i<9;i++)
printf("第%d個皇后:%d\n",i,a[i]);
printf("\n\n");
} } } } } } } } }
/////////////////////////////////Check函式功能:檢驗第n行的皇后是否和之前的皇后有衝突,沒有的話返回1
int Check(int a[],int n)
{
for(int i=1;i<n;i++)
{
if(abs(a[i]-a[n])==abs(i-n) || a[i]==a[n])//////////////見下面註釋
return 0;
}
return 1;
}
程式碼註釋:
某一行的皇后a[n]不能和之前的皇后a[i]位置有衝突,約束條件為:
a、不在同一列:a[n] != a[i]
b、不在同一行:因為現在是每一行求一個皇后的位置,所以同一行不會有衝突,不需要考慮。
c、不在同一對左角線:a[n]-a[i] != n-i
d、不在同一右對角線:a[n]-a[i] != -(n-i)
條件c和d可以合成:abs(a[n]-a[i]) != abs(n-i)
總結:其實這裡用到的就是深度優先搜尋的思想,從第一行的皇后一直深入去找第二行、第三行...皇后的位置。其中加上了約束條件Check函式進行“剪枝”。這就是回溯演算法的思想:深度優先搜尋,遇到不滿足的情況就進行回溯。
2、方法一的優化。上述程式碼易讀、易懂,但是用八個for迴圈不免顯得很累贅,而且如果要求在100*100的棋盤上放100個皇后這種“N皇后問題“呢?難道用100個for迴圈嗎?我們來把程式碼優化一下,用到的思想還是和方法一相同:深度優先搜尋、回溯。具體程式碼如下:
void main()
{
int a[256]={0};
int i=1,j,n,t=1;////////////////////////////////////i表示當前正在搜尋第i行的皇后位置
printf("請輸入幾皇后?n=");
scanf("%d",&n);
while(i>0)
{
for(a[i]++;a[i]<=n;a[i]++)
{
if(Check(a,i))//////////////////////////////如果第i行的皇后與之前的皇后位置上沒有衝突,則break跳出迴圈
break;
}
if(a[i]<=n)/////////////////////////////////////如果a[i]<=n,即上面的for迴圈是由“break;”跳出來的,即第i行皇后的位置符合條件
{
if(i==n)////////////////////////////////////找到一組解,輸出
{
printf("第%d種解法:\n",t++);
for(j=1;j<=n;j++)
printf("第%d個皇后:%d\n",j,a[j]);
printf("\n\n");
}
else////////////////////////////////////////未找完
{
i++;
a[i]=0;
}
}
else
i--;////////////////////////////////////////回溯
}
}
程式碼註釋:上面用到的Check函式和方法一的Check函式相同。
總結:雖然上面程式碼中只用到兩層迴圈,但是思想、思路和方法一都是一樣的,時間複雜度也是和方法一的時間複雜度相同。當n大於10之後運算就已經比較困難了。
3、遞迴實現。上面兩種方法都是用到了深度優先搜尋,而一般而言,深度優先搜尋都是可以用遞迴來實現的。下面我們用遞迴的方式解決八皇后問題。具體程式碼如下:
int a[20],n,i,t=1;////////////////////////////////////////全域性變數
void Try(int i)
{
int j,k;
for(j=1;j<=n;j++)
{
a[i]=j;
if(Check(a,i))///////////////////////////////////////如果第j列不會與之前的皇后衝突
{
if(i<n)//////////////////////////////////////////如果i<n,即還沒有找到八個皇后,繼續遞迴
Try(i+1);
else ////////////////////////////////////////////如果找到了一組解就輸出
{
printf("第%d種解法:\n",t++);
for(k=1;k<=n;k++)
printf("第%d個皇后:%d\n",k,a[k]);
printf("\n\n");
} } } }
void main()
{
printf("幾皇后?n=");
scanf("%d",&n);
Try(1);
}
程式碼註釋:
a、此處遞迴的思路很簡單,每一層遞迴表示一行皇后,j表示列,即a[i]=j表示第i行的皇后位置在第j列。
b、以上用到的Check函式與方法一的Check函式相同。
我不清楚是什麼原因,遞迴的速度竟然明顯比前面的兩種方法快??
轉載請註明出處,謝謝!(原文連結:http://blog.csdn.net/bone_ace/article/details/41419695)
相關文章
- 回溯法(排列樹)解決八(N)皇后問題
- js使用遞迴回溯法解八皇后問題程式碼分享JS遞迴
- 每天刷個演算法題20160519:回溯法解八皇后演算法
- 從八皇后問題到回溯演算法演算法
- 用棧+回溯+非遞迴解決N皇后問題遞迴
- 《演算法》系列—大白話聊分治、回溯,手撕八皇后演算法
- 回溯法解決迷宮問題
- 回溯法解決喝酒問題 (轉)
- python八皇后Python
- 使用回溯演算法解決N皇后問題以及間隔排列問題演算法
- 回溯法解決全排列問題總結
- 資料結構和演算法——遞迴-八皇后問題(回溯演算法)資料結構演算法遞迴
- 回溯演算法 | 追憶那些年曾難倒我們的八皇后問題演算法
- 八皇后||演算法演算法
- js解八皇后問題程式碼例項JS
- 洛谷八皇后問題
- 常用演算法之回溯法演算法
- (回溯法)解決一系列組合問題
- 國際象棋“皇后”問題的回溯演算法演算法
- 回溯法
- 八皇后問題python解法Python
- 八皇后問題自我總結
- 經典演算法之回溯法演算法
- 八皇后問題分析和實現
- C#資料結構與演算法系列(十四):遞迴——八皇后問題(回溯演算法)C#資料結構演算法遞迴
- 每日一題之拉低通過率 回溯演算法 leetcode 51 N皇后每日一題演算法LeetCode
- Python 八皇后解法(非遞迴版本)Python遞迴
- 資料結構 八皇后 c swift 版本資料結構Swift
- 洛谷 P1219 八皇后
- leetcode題解(遞迴和回溯法)LeetCode遞迴
- 五大常用演算法之四:回溯法演算法
- 八皇后問題的錯誤程式碼示範
- Leetcode 通過率最高的困難題 N皇后 II 【回溯解法-剪枝】LeetCode
- IIS 之 無法找到該頁 解決辦法
- 畢設之錯誤解決辦法
- 回溯法求迷宮問題
- 轉:八皇后問題 java實現,演算法兩則Java演算法
- Axure之解決文字框無法輸入