大致思路:
其实就是三个功能函数: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 Input
1850
Sample Output
19210
Author cgf
Source
2008 HZNU Programming Contest
问题链接:HDU2553 N皇后问题。
问题简述:(略)。
问题分析:这是一个经典的回溯法程序,是用递归来实现的,其实也可以用非递归来实现。回溯法并不是必须用递归来实现的。
程序说明:程序的细节还是需要注意的,例如打表时,需要多声明一个元素,因为数组下标是从0开始。
原先做过解N皇后问题的程序,就拿来简单改写了一下。有关程序,参见:八皇后(N皇后)问题算法程序。
一提交,“Time Limit Exceeded”,只好先打表。原来的代码注释留在那里了。
AC的C语言程序如下:
[cpp]
view plain
copy
- /* 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;
- }
- while(scanf(“%d”, &n) != EOF) {
- // 判断结束条件
- if(n == 0)
- break;
- printf(“%d\n”, ans[n]);
- }
- return 0;
- }