HDU 2553 N皇后问题(2种详细题解)

21 篇文章 1 订阅

     N皇后问题

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5654    Accepted Submission(s): 2555


Problem Description
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。

 

 

Input
共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。
 

 

Output
共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。
 

 

Sample Input
1
8
5
0
 

 

Sample Output
1
92
10


 这是一道深搜题目!问题的关键是在剪枝。

下面我们对问题进行分析:

1.一行只能放一个皇后,所以我们一旦确定此处可以放皇后,那么该行就只能放一个皇后,下面的就不要再搜了。

2.每一列只能放一个皇后,所以我们下次搜索就不要再搜已经放过的皇后了。

3.斜的45°线也只能放一个。

综上如何才能最快速的确定一列和45°是否用过这个是个关键步骤,一旦此步骤确定我们就可以很快的进行搜索了。

我们用三个数组来保存他的每一个状态及(三个方向 ↖ ↑ ↗)

但是如果我们保存↑(每一列方向上的皇后)是非常容易保存的 但是保存( 这两个方向上的状态就不容易了↖ ↗)

 

再分析,在这个(↖)方向上的数据的行和列有什么特点

 0  1  2  3   4

-1  0  1  2  3

-2 -1  0  1  2

-3 -2 -1  0  1

-4 -3 -2 -1  0 

将此表列出我们就应该知道在(↖)方向上的数据的行和列的特点了,及   在 (↖)方向上  列 - 行 的差是相等的。

假如我们用数组保存负数肯定是不行的, 所以我们要加上 n,让他变为非负.

 

再分析,在这个( ↗)方向上的数据的行和列有什么特点

0 1 2 3 4

1 2 3 4 5

2 3 4 5 6

3 4 5 6 7

将此表列出我们就应该知道在(↗)方向上的数据的行和列的特点了,及   在 (↗)方向上  列 + 行 的和是相等的。

知道数据怎么处理就可以解决问题了。

#include<bits/stdc++.h>  
using namespace std;  
const int maxn = 25;  
int ans[maxn], n, sum;  
bool vis[3][maxn];  
  
void dfs(int cur)  
{  
    if(cur == n+1) { sum++; return ; }  
    for(int i = 1; i <= n; i++)  
    {  
        if(!vis[0][i] && !vis[1][i-cur+n] && !vis[2][i+cur])  
        {  
            vis[0][i] = vis[1][i-cur+n] = vis[2][i+cur] = 1;  
            dfs(cur+1);  
            vis[0][i] = vis[1][i-cur+n] = vis[2][i+cur] = 0;  
        }  
    }  
}  
  
int main(void)  
{  
    for(n = 1; n <= 10; n++)  
    {  
        memset(vis, 0, sizeof(vis));  
        sum = 0;  
        dfs(1);  
        ans[n] = sum;  
    }  
    while(cin >> n, n)  
        printf("%d\n", ans[n]);  
    return 0;  
}  

这道题最直接的思路就是枚举 暴力解决,但是显然是不行的。

      所以只能用回溯。 基本思路 ,一行一行的放 皇后, 然后再递归判断是否与之前已放好的皇后有冲突,一旦有冲突,则不需要继续下一行的搜索,直接返回(省去不必要的枚举)。

       另外关于这题,还有一点,我第一次交的时候TLE了,说明测试数据特别多。所以得先预处理(这个亏吃了很多次了,牢记牢记).

//八皇后问题 回溯法
#include<stdio.h>
int tot=0,row,line[10],n;
int main()
{
    void search(int );
    int a[11];
    for(n=1;n<=10;n++) //之前就是没有这一步预处理,所以TLE了 TT
    {
        tot=0;
        search(0);
        a[n]=tot;
    }
    while(scanf("%d",&n)!=EOF&&n)
         printf("%d\n",a[n]);
    return 0;
}
void search(int row) //递归搜索可行解
{
    int i,j;
     if(row==n) tot++; //当row=n时,说明每一行的皇后都不冲突,即为可行解
     else
     for(i=0;i<n;i++)
     {
         int ok=1;
         line[row]=i; //尝试把第row行的皇后放在i列上
         for(j=0;j<row;j++) //检验是否与前面已放好的皇后冲突
         {
             if(line[row]==line[j]||line[row]-row==line[j]-j||line[row]+row==line[j]+j)
             {
                 ok=0; 
                 break; //,跳出最内层循环如果冲突,停止搜索,返回上一级递归回溯。回溯法高效的关键。
             }
         }
         if(ok)
             search(row+1);
     }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值