【藍橋杯-遞歸回溯】八皇后問題+N皇后問題
大致思路:
其實就是三個功能函式:place attack output_solution
place函式中的任務就是把所有的(設為有maxqueen個)皇后的列位置安頓好。其傳入的引數僅一個,為皇后的序數q,然後經過i從1~maxqueen的遍歷找到該序數q的皇后應在的列數號,使queen[q]=i,條件就是attack(q,i)==false。(那麼該皇后的行數號呢?這個要理解,皇后的行數號即其序數號,即q。) 設好列數號之後判斷一下是否q==maxqueen-1也就是最後一個皇后放好了,若是,則呼叫output_solution輸出該最終棋盤佈局。
attack函式就是判斷皇后衝不衝突的。其實就是判斷在他(q)之前放好的皇后的列數和q的列數(queen[q])相不相等,然後看看是不是在同一對角線上。
output_solution就是輸出最終的棋盤佈局。
- /*
- *
- * 【問題描述】在一個8×8的國際象棋棋盤上放置8個皇后,
- * 要求每個皇后兩兩之間不“衝突”,即沒有一個皇后能“吃
- * 掉”任何其他一個皇后,簡單的說就是沒有任何兩個皇后
- * 佔據棋盤上的同一行或同一列或同一對角線,即在每一橫
- * 列、豎列、斜列都只有一個皇后。
- *
- * 遞迴法求出8個皇后問題的解
- * 本程式使用一維陣列表示皇后的位置,queen[i]的值表示第i行皇后所在的列
- *
- * 本程式通過修改巨集定義MAXQUEEN的值,可以解決N皇后問題。
- *
- */
- #include <stdio.h>
- #include <conio.h>
- #define TRUE 1
- #define FALSE 0
- #define MAXQUEEN 8
- #define ABS(x) ((x>0)?(x):-(x)) /*求x的絕對值*/
- /*存放8個皇后的列位置,陣列下標為皇后的列位置*/
- int queen[MAXQUEEN];
- int total_solution = 0; /*計算共有幾組解*/
- /*函式原型宣告*/
- void place(int);
- int attack(int,int);
- void output_solution();
- int main(void)
- {
- place(0); /*從第0個皇后開始擺放至棋盤*/
- return 0;
- }
- /* 遞迴放置皇后子程式 */
- void place(int q) //關鍵!
- {
- int i=0;
- while(i<MAXQUEEN)
- {
- if(!attack(q, i)) /* 皇后未受攻擊 */
- {
- queen[q]=i; /* 儲存皇后所在的列位置 */
- /* 判斷是否找到一組解 */
- if(q==MAXQUEEN-1)
- output_solution(); /* 輸出此組解 */
- else
- place(q+1); /* 否則繼續擺下一個皇后 */
- }
- i++;
- }
- }
- /* 測試在(row,col)上的皇后是否遭受攻擊若遭受攻擊則返回值為1,否則返回0 */
- int attack(int row, int col)
- {
- int i, atk=FALSE;
- int offset_row, offset_col;
- i=0;
- while(!atk && i<row)
- {
- offset_row=ABS(i-row);
- offset_col=ABS(queen[i]-col);
- /* 判斷兩皇后是否在同一列,是否在同一對角線 */
- /* 若兩皇后在同列或同對角線,則產生攻擊,atk==TRUE */
- atk = (queen[i] == col) || (offset_row == offset_col);
- i++;
- }
- return atk;
- }
- /* 輸出8個皇后的解 */
- void output_solution()
- {
- int x,y;
- total_solution += 1;
- printf("Solution#%3d\n\t",total_solution);
- for(x=0;x<MAXQUEEN;x++)
- {
- for(y=0;y<MAXQUEEN;y++)
- if(y==queen[x])
- printf("Q"); /* 用字母Q表示皇后 */
- else
- printf("-"); /* 用-表示空白 */
- printf("\n\t");
- }
- printf("\n");
- getchar();
- }
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int q[9];
int maxx=0;
int a[9][9];
bool attack(int p,int qq)
{
if(p==1)
return false;
else
{
for(int i=1;i<p;i++)
{
if(q[i]==qq)
return true;
if(abs(q[i]-qq)==abs(i-p))
return true;
}
return false;
}
}
void place(int p)
{
if(p==9)
{
int sum=0;
for(int i=1;i<=8;i++)
{
sum+=a[i][q[i]];
}
if(maxx<sum)
maxx=sum;
return;
}
for(int i=1;i<=8;i++) //對第p行的列上遞迴
{
if(attack(p,i)==false)
{
q[p]=i;
place(p+1);
}
}
}
int main()
{
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++)
cin>>a[i][j];
memset(q,0,sizeof(q));
place(1);
cout<<maxx;
return 0;
}
有必要理解的是,這輸出的是多個解。因為place函式裡的那個遞迴啊,之後又i++,也就是序數為q的同一個皇后就算在這一列可以放上,那就將就著去進入遞迴,弄出一種解決方案,但是!之後仍由著while語句遍歷去往下一行嘗試。
這就是遞迴的魅力。你在leetcode上求subset的遞迴也是這個理兒啊親——求多種可能!今天明白了原來這叫回溯法
而對於N皇后問題,其實就是把maxQUEEN換成給定的n即可。
以下是原作者海島Blog發現因為題目要輸入多個n求對應的解決方案的個數,每次都去重新算導致Time Limit Exceeded,於是打表 一次性算出1~自己取的num,到時候輸入n(題中輸入的n<num)直接取ans[n]即可。
N皇后問題
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 27039 Accepted Submission(s): 12035
Problem Description在N*N的方格棋盤放置了N個皇后,使得它們不相互攻擊(即任意2個皇后不允許處在同一排,同一列,也不允許處在與棋盤邊框成45角的斜線上。
你的任務是,對於給定的N,求出有多少種合法的放置方法。
Input共有若干行,每行一個正整數N≤10,表示棋盤和皇后的數量;如果N=0,表示結束。
Output共有若干行,每行一個正整數,表示對應輸入行的皇后的不同放置數量。
Sample Input1850
Sample Output19210
Authorcgf
Source
問題簡述:(略)。
程式說明:程式的細節還是需要注意的,例如打表時,需要多宣告一個元素,因為陣列下標是從0開始。
原先做過解N皇后問題的程式,就拿來簡單改寫了一下。有關程式,參見:八皇后(N皇后)問題演算法程式。
一提交,“Time Limit Exceeded”,只好先打表。原來的程式碼註釋留在那裡了。
AC的C語言程式如下:
- /* HDU2553 N皇后問題 */
- #include <stdio.h>
- #define TRUE 1
- #define FALSE 0
- #define MAXQUEEN 10
- #define ABS(x) ((x>0)?(x):-(x)) /*求x的絕對值*/
- /*存放8個皇后的列位置,陣列下標為皇后的列位置*/
- int queen[MAXQUEEN];
- int total_solution; /*計算共有幾組解*/
- /* 測試在(row,col)上的皇后是否遭受攻擊若遭受攻擊則返回值為1,否則返回0 */
- int attack(int row, int col)
- {
- int i, atk=FALSE;
- int offset_row, offset_col;
- i=0;
- while(!atk && i<row)
- {
- offset_row=ABS(i-row);
- offset_col=ABS(queen[i]-col);
- /* 判斷兩皇后是否在同一列,是否在同一對角線 */
- /* 若兩皇后在同列或同對角線,則產生攻擊,atk==TRUE */
- atk = (queen[i] == col) || (offset_row == offset_col);
- i++;
- }
- return atk;
- }
- /* 遞迴放置皇后子程式 */
- void place(int q, int n)
- {
- int i=0;
- while(i < n)
- {
- if(!attack(q, i)) /* 皇后未受攻擊 */
- {
- queen[q]=i; /* 儲存皇后所在的列位置 */
- /* 判斷是否找到一組解 */
- if(q == n-1)
- total_solution++; /* 得到一個解 */
- else
- place(q+1, n); /* 否則繼續擺下一個皇后 */
- }
- i++;
- }
- }
- int main(void)
- {
- int n;
- int ans[MAXQUEEN+1], i;
- // 因為“Time Limit Exceeded”,只好先打表
- for(i=1; i<=MAXQUEEN; i++) {
- // 皇后遍歷
- total_solution = 0;
- place(0, i); /*從第0個皇后開始擺放至棋盤*/
- ans[i] = total_solution;