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

来源:互联网 发布:mac怎么看运行的程序 编辑:程序博客网 时间:2024/06/01 10:52

     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 0
原创粉丝点击