HDU:2553 n皇后问题

N皇后问题

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


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

解题思路:

首先要回求解n皇后问题,其次就是题目有一个小陷阱,输入的n只是从1~10,但是后面的测试数据,会反复测1~10这些数,则如果输入一个n就求一次解,那么最终会导致超时,因此想将1~10皇后问题的解都制表,然后根据输入的n进行快速访问就可以了。

求解n皇后问题主要用的是回溯的思想,在放的时候要注意判断,一个皇后的同行同列以及斜线上都不能有其他的皇后,因此如果递归到某一位置发现它不能放置皇后,那么就要进行回溯。以四皇后为例。

如图为程序开始,先确定列再确定行,先从第一列开始,发现第一行经过判断后可以放皇后则放置第一个皇后,放置完此皇后后,则图中红线所画过的方格不能再放置皇后了,第一列已经放过皇后了,就要考虑第二列,然后从第一行开始考虑,发现第一行不能放,第二行不能放,直到第三行,发现可以放置皇后,则放置皇后,放置完后如下图所示:

第二个皇后放置完后,同样的先将不能再放置皇后的空格标记,即图中红线所划过的空格,此时开始搜索第三列,发现第一行不能放皇后,第二行也不能放皇后,第三行不能放置皇后,第四行也不能放置皇后,则发现第三列的任一行都不能放置皇后,此时就要进行回溯了,则2列3行不放置皇后(因为这种方法没有解),则我会恢复成第一个图片的状态。此时回溯回来后,就要考虑第2列第4行是否能放置皇后,从图1可以看出,是可以的,则在第2列第4行放置皇后,如下图:

在2列4行放置完皇后后如图红色线所划过的空格都不能再放皇后了,此时考虑在第三列放置皇后。发现从第1行开始考虑,到第2行发现了一个能放皇后的位置,就在第3列第2行放置皇后,然后再次更新不能放置皇后的格子,更新后的状态如下图所示:

此时递归更深一层要在第4列放置皇后,经过考虑后,第4列1~4行都不能放置皇后,则要回溯,将3列2行的格子不放置皇后,考虑3~4这些位置,发现也不能放置皇后,再次回溯,将2列4行的位置恢复,此时第二列也考虑完了,再次回溯,将1行1列的位置恢复,考虑在2~4行放置皇后,则

发现可以在第2行放置皇后。经过一系列回溯之后,放置皇后的状态如下所示:

同样的开始考虑在第二列可以安放皇后的位置放置皇后,大概思路就是这样,能走下去就进行深一层的递归,不能走下去就进行回溯。

#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;

int n;               //问题规模
int ans;             //解的个数
int vis[12][12];     //标记数组
int row[12],col[12]; //标记行,标记列
int table[12];
bool check(int i,int j) {
    int s,t;
    if(row[i]) return false;   //这一行已有皇后,该位置不合法。
    if(col[j]) return false;   //这一列已有皇后,该位置不合法。
    //判断(i,j)的斜线是否有皇后。
    //判断左上角有没有皇后。
    for(s=i-1,t=j-1;s>=0&&t>=0;s--,t--) {
        if(vis[s][t]) return false;
    }
    //判断右下角有没有皇后。
    for(s=i+1,t=j+1;s<n&&t<n;s++,t++) {
        if(vis[s][t]) return false;
    }
    //判断左下角有没有皇后
    for(s=i+1,t=j-1;s<n&&t>=0;s++,t--) {
        if(vis[s][t]) return false;
    }
    //判断右下角有没有皇后
    for(s=i-1,t=j+1;s>=0&&t<n;s--,t++) {
        if(vis[s][t]) return false;
    }
    return true;
}
void dfs(int j) {
    if(j == n) {
        ans++;
        return;
    }
    for(int i = 0; i < n; i++) {
        bool flag = check(i,j);  //检测i行j列是否可以放皇后
        if(flag) {
            vis[i][j] = 1;       //在i行j列放置皇后
            row[i] = 1;
            col[j] = 1;
            dfs(j+1);            //考虑在下一列放皇后
            vis[i][j] = 0;
            row[i] = 0;
            col[j] = 0;
        }
    }
}
int main() {
    for(int i = 1; i <= 10; i++) {
        memset(vis,0,sizeof(vis));
        memset(row,0,sizeof(row));
        memset(col,0,sizeof(col));
        n = i;
        ans = 0;
        dfs(0);
        table[i] = ans;
    }
    while(~scanf("%d",&n)) {
        if(n == 0) break;
        printf("%d\n",table[n]);
    }
    return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值