【蓝桥杯-递归回溯】八皇后问题+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就是输出最终的棋盘布局。

  1. /* 
  2.  * 
  3.  * 【问题描述】在一个8×8的国际象棋棋盘上放置8个皇后, 
  4.  * 要求每个皇后两两之间不“冲突”,即没有一个皇后能“吃 
  5.  * 掉”任何其他一个皇后,简单的说就是没有任何两个皇后 
  6.  * 占据棋盘上的同一行或同一列或同一对角线,即在每一横 
  7.  * 列、竖列、斜列都只有一个皇后。 
  8.  * 
  9.  * 递归法求出8个皇后问题的解 
  10.  * 本程序使用一维数组表示皇后的位置,queen[i]的值表示第i行皇后所在的列 
  11.  * 
  12.  * 本程序通过修改宏定义MAXQUEEN的值,可以解决N皇后问题。 
  13.  * 
  14.  */  
  15.   
  16. #include <stdio.h>  
  17. #include <conio.h>  
  18.   
  19. #define TRUE 1  
  20. #define FALSE 0  
  21. #define MAXQUEEN 8  
  22. #define ABS(x) ((x>0)?(x):-(x))  /*求x的绝对值*/  
  23.   
  24. /*存放8个皇后的列位置,数组下标为皇后的列位置*/  
  25. int queen[MAXQUEEN];  
  26. int total_solution = 0;  /*计算共有几组解*/  
  27.   
  28. /*函数原型声明*/  
  29. void place(int);  
  30. int attack(int,int);  
  31. void output_solution();  
  32.   
  33. int main(void)  
  34. {  
  35.     place(0); /*从第0个皇后开始摆放至棋盘*/  
  36.   
  37.     return 0;  
  38. }  
  39.   
  40. /* 递归放置皇后子程序 */  
  41. void place(int q)  //关键!
  42. {  
  43.     int i=0;  
  44.     while(i<MAXQUEEN)  
  45.     {  
  46.         if(!attack(q, i)) /* 皇后未受攻击 */  
  47.         {  
  48.             queen[q]=i; /* 储存皇后所在的列位置 */  
  49.             /* 判断是否找到一组解 */  
  50.             if(q==MAXQUEEN-1)  
  51.                 output_solution(); /* 输出此组解 */  
  52.             else  
  53.                 place(q+1); /* 否则继续摆下一个皇后 */  
  54.         }  
  55.         i++;  
  56.     }  
  57. }  
  58.   
  59. /* 测试在(row,col)上的皇后是否遭受攻击若遭受攻击则返回值为1,否则返回0 */  
  60. int attack(int row, int col)  
  61. {  
  62.     int i, atk=FALSE;  
  63.     int offset_row, offset_col;  
  64.     i=0;  
  65.     while(!atk && i<row)  
  66.     {  
  67.         offset_row=ABS(i-row);  
  68.         offset_col=ABS(queen[i]-col);  
  69.         /* 判断两皇后是否在同一列,是否在同一对角线 */  
  70.         /* 若两皇后在同列或同对角线,则产生攻击,atk==TRUE */  
  71.         atk = (queen[i] == col) || (offset_row == offset_col);  
  72.         i++;  
  73.     }  
  74.     return atk;  
  75. }  
  76.   
  77. /* 输出8个皇后的解 */  
  78. void output_solution()  
  79. {  
  80.     int x,y;  
  81.     total_solution += 1;  
  82.     printf("Solution#%3d\n\t",total_solution);  
  83.     for(x=0;x<MAXQUEEN;x++)  
  84.     {  
  85.         for(y=0;y<MAXQUEEN;y++)  
  86.         if(y==queen[x])  
  87.             printf("Q"); /* 用字母Q表示皇后 */  
  88.         else  
  89.             printf("-"); /* 用-表示空白 */  
  90.         printf("\n\t");  
  91.     }  
  92.     printf("\n");  
  93.   
  94.     getchar();  
  95. }  

自己现写的代码如下:

#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


问题链接HDU2553 N皇后问题

问题简述:(略)。

问题分析:这是一个经典的回溯法程序,是用递归来实现的,其实也可以用非递归来实现。回溯法并不是必须用递归来实现的。

程序说明:程序的细节还是需要注意的,例如打表时,需要多声明一个元素,因为数组下标是从0开始。

原先做过解N皇后问题的程序,就拿来简单改写了一下。有关程序,参见:八皇后(N皇后)问题算法程序
一提交,“Time Limit Exceeded”,只好先打表。原来的代码注释留在那里了。


AC的C语言程序如下:

[cpp]  view plain  copy
  1. /* HDU2553 N皇后问题 */  
  2.   
  3. #include <stdio.h>  
  4.   
  5. #define TRUE 1  
  6. #define FALSE 0  
  7. #define MAXQUEEN 10  
  8. #define ABS(x) ((x>0)?(x):-(x))  /*求x的绝对值*/  
  9.   
  10. /*存放8个皇后的列位置,数组下标为皇后的列位置*/  
  11. int queen[MAXQUEEN];  
  12. int total_solution;  /*计算共有几组解*/  
  13.   
  14. /* 测试在(row,col)上的皇后是否遭受攻击若遭受攻击则返回值为1,否则返回0 */  
  15. int attack(int row, int col)  
  16. {  
  17.     int i, atk=FALSE;  
  18.     int offset_row, offset_col;  
  19.     i=0;  
  20.     while(!atk && i<row)  
  21.     {  
  22.         offset_row=ABS(i-row);  
  23.         offset_col=ABS(queen[i]-col);  
  24.         /* 判断两皇后是否在同一列,是否在同一对角线 */  
  25.         /* 若两皇后在同列或同对角线,则产生攻击,atk==TRUE */  
  26.         atk = (queen[i] == col) || (offset_row == offset_col);  
  27.         i++;  
  28.     }  
  29.     return atk;  
  30. }  
  31.   
  32. /* 递归放置皇后子程序 */  
  33. void place(int q, int n)    
  34. {  
  35.     int i=0;  
  36.     while(i < n)  
  37.     {  
  38.         if(!attack(q, i))           /* 皇后未受攻击 */  
  39.         {  
  40.             queen[q]=i;             /* 储存皇后所在的列位置 */  
  41.             /* 判断是否找到一组解 */  
  42.             if(q == n-1)  
  43.                 total_solution++;   /* 得到一个解 */  
  44.             else  
  45.                 place(q+1, n);      /* 否则继续摆下一个皇后 */  
  46.         }  
  47.         i++;  
  48.     }  
  49. }  
  50.   
  51. int main(void)  
  52. {  
  53.     int n;  
  54.     int ans[MAXQUEEN+1], i;  
  55.   
  56.     // 因为“Time Limit Exceeded”,只好先打表  
  57.     for(i=1; i<=MAXQUEEN; i++) {  
  58.         // 皇后遍历  
  59.         total_solution = 0;  
  60.         place(0, i);                /*从第0个皇后开始摆放至棋盘*/  
  61.   
  62.         ans[i] = total_solution;  
  63.     }  
  64.   
  65.   
  66.     while(scanf("%d", &n) != EOF) {  
  67.         // 判断结束条件  
  68.         if(n == 0)  
  69.             break;  
  70.         printf("%d\n", ans[n]);  
  71.     }  
  72.   
  73.     return 0;  
  74. }  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值