N 皇后问题 回溯/深搜

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

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

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 15
using 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;
        }
     }
     
}






 


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值