N 皇后问题 回溯/深搜

来源:互联网 发布:配送软件 编辑:程序博客网 时间:2024/06/04 18:47

深度优先搜索:例如走迷宫,你没有办法用分身术来站在每个走过的位置,不撞南山不回头,利用栈这种数据结构来实现(利用递归便于实现,但是效率较低),找到的第一个解不一定是最优解,只是先序遍历最早的可行解。 

回溯法试探法:按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。在现实中,有很多问题往往需要我们把其所有可能穷举出来,然后从中找出满足某种要求的可能或最优的情况,从而得到整个问题的解。回溯算法就是解决这种问题的“通用算法”,有“万能算法”之称。

Problem Description 
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。 
你的任务是,对于给定的N,求出有多少种合法的放置方法。 
Input 
共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。 
Output 
共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。 
Sample Input 




Sample Output 

92 
10

思路:根据深搜的模板来嵌套即可,唯一需要注意的就是判断条件。以行为标准,则不用判断行,直接判断列与斜线即可。

代码即是把上述内容表述出来。

本题存在一个小坑,就是应该用数组存储N皇后的结果,然后打表输出。

#include<cstdio>#include<cstrin>using namespace std;int map[12][12];int cnt=0;int n;bool ok(int i,int y){    for(int j=0;j<=y;j++)    {//j描述行,因为一行行标点,所以只要判断前y点就好        if(map[j][i]==1)//判断同一列是否满足条件            return false;        if(y-j>=0&&i-j>=0)//防止越界            if(map[y-j][i-j]==1)//判断左上斜线               return false;        if(y-j>=0&&i+j<=n)//防止越界            if(map[y-j][i+j]==1)//判断右上斜线               return false;    }    return true;}void dfs(int y){    if(y==n){//结束递归条件        cnt++;    }    for(int x=0;x<n;x++){//x为列数,一列列填点。        if(ok(x,y)){//如果该点满足条件,填充。            map[y][x]=1;            dfs(y+1);            map[y][x]=0;//恢复棋盘。        }    }}int main(){    int ans[11]={0};    for(n=1;n<=10;n++)    {            cnt=0;            dfs(0);//搜索从0行开始            ans[n]=cnt;    }    while(~scanf("%d",&n)&&n)        printf("%d\n",ans[n]);    return 0;}
根据上述的思路,可以进行空间复杂的简化

即用一个x[]数组存储的列,而下标代表行,判断斜线上的即可用绝对值的差值即可,次算法优化了存储空间,但对时间复杂度无太大影响都为31MS

#include <cstdio>#include <cmath>#include <cstring>#define N 15using namespace std;int x[N],result[N];          //皇后放置的列数int n;            //皇后个数int sum=0;        //可行解个数int place(int k){int i;for(i=1;i<k;i++)       if(abs(k-i)==abs(x[k]-x[i])||x[k]==x[i])   return 0;   return 1;}int queen(int k){int i;if(k>n)sum++;else   for(i=1;i<=n;i++)   {   x[k]=i;   if(place(k))   queen(k+1);   }   return sum;}int main(){for(n=1;n<=10;n++)    {       result[n]=queen(1);       sum=0;    }while(scanf("%d",&n)==1&&n!=0)        printf("%d\n",result[n]);return 0;}

判断斜线上是否存在皇后,还有一种数学技巧,左上右下或左下右上斜线上共有2*n-1种情况

#include <iostream>#include <cstring>using namespace std;int n;int ans=0;bool a[20];  //列使用情况 true 用bool x1[20]; //左上*右下的情况bool y1[20]; //左下+右上的情况void dfs(int deep){     if(deep>=n){        ans++;      return ;      }     for(int i=0;i<n;i++){        if(x1[i+deep]==false&&y1[i-deep+n]==false&&a[i]==false)        {            x[deep+i]=true;            y1[i-deep+n]=true;            a[i]=true;                        dfs(deep+1);                         x[deep+i]=false;            y1[i-deep+n]=false;            a[i]=false;        }     }     }






 


原创粉丝点击