N皇后问题

N皇后问题,是指在一个N*N的国际象棋棋盘上放置n个皇后,使得这n个皇后两两均不在同一行、同一列、同一条对角线上,求合法的方案数。如下图:n=5的情况

对于这个问题,如果采用组合数的方式来枚举每一种情况(即从n²个位置中选择n个位置),那么将需要\binom{n}{n\times n}的枚举量,当n=8是就是54 502 232次枚举,如果n更大,那么就会无法承受。

但是换个思路,考虑到每行每列只能放置一个皇后,那么如果吧n列皇后所在的行号依次写出,那么就会是1~n的一个排列。例如对于4-4a来说对应的排列就是24135,图4-4b就是35142.于是就只需要枚举1~n的所有排列,查看每个排列对应的放置方案是否合法,统计其中合法的方案即可。由于总共有n!个排列,因此当n=8是只需要40320次枚举,比之前的做法优秀许多。

于是可以再全排列的代码基础上进行求解,由于当到达递归边界时表示生成了一个排列,所以需要在其内部判断是否为合法方案,即遍历每两个皇后,判断他们是否在同一条对角线上,若不是,则累计count。

 

  1. 暴力枚举法
    #include<cstdio>
    #include<stdlib.h>
    int count=0;
    int n;
    int P[8],hashTable[8]= {false};
    void generateP(int index)
    {
        if(index==n+1)//递归边界,生成一个排列
        {
            bool flag=true;//flag为true表示当前排列为一个合法方案
            for(int i=1; i<=n; i++)//遍历任意两个皇后
            {
                for(int j=i+1; j<=n; j++)
                {
                    if(abs(i-j)==abs(P[i]-P[j]))//如果在一条对角线上
                        flag=false;//不合法
                }
            }
            if(flag)//打印输出
            {
                for(int i=1; i<=n; i++)
                    printf("%d",P[i]);
                printf("\n");
                count++;//若当前方案合法count+1
                return;
            }
        }
        for(int x=1; x<=n; x++)
        {
            if(hashTable[x]==false)
            {
                P[index]=x;
                hashTable[x]=true;
                generateP(index+1);
                hashTable[x]=false;
            }
        }
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            generateP(1);
            printf("%d皇后可行方案:%d种\n",n,count);
        }
        return 0;
    }
    

  2. 回溯法

实际上,通过思考(动手分析)可以发现,当已经放置了一部分皇后时,可能剩余的皇后无论怎样放置都不可能合法,此时就没必要往下递归了,直接放回上层即可,这样可以减少很多计算量。

#include<cstdio>
#include<stdlib.h>
int count=0;
int n;
int P[8],hashTable[8]= {false};
void generateP(int index)
{
    if(index==n+1)//递归边界,生成一个排列
    {
        for(int i=1; i<=n; i++)
            printf("%d",P[i]);
        printf("\n");
        count++;//能到达这里一定是合法的
        return;
    }
    for(int x=1; x<=n; x++)//第x行
    {
        if(hashTable[x]==false)//第x行还没有皇后
        {
            bool flag=true;//flag为true表示当前皇后不会和之前的皇后冲突
            for(int pre=1;pre<index;pre++)//遍历之前的皇后
            {//第index列皇后的行号为x,第pre列皇后的行号为P[pre]
                if(abs(index-pre)==abs(x-P[pre]))
                {
                    flag=false;//与之前的皇后在一条对角线,冲突
                    break;
                }
            }
            if(flag)//如果可以把皇后放在第x行
            {
                P[index]=x;//令第index列皇后的行号为x
                hashTable[x]=true;//第x行已经被占用
                generateP(index+1);//递归处理第index+1行皇后
                hashTable[x]=false;//递归完毕,还原第x行为未占用
            }
        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        generateP(1);
        printf("%d皇后可行方案:%d种\n",n,count);
    }
    return 0;
}

 

例题:

问题 D: 八皇后

时间限制: 1 Sec  内存限制: 32 MB
提交: 1077  解决: 595
[提交][状态][讨论版][命题人:外部导入]

题目描述

 

会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。 
对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2...b8,其中bi为相应摆法中第i行皇后所处的列数。已经知道8皇后问题一共有92组解(即92个不同的皇后串)。
给出一个数b,要求输出第b个串。串的比较是这样的:皇后串x置于皇后串y之前,当且仅当将x视为整数时比y小。

 

输入

 

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数b(1 <= b <= 92)

 

输出

 

输出有n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串。

 

样例输入

3
6
4
25

样例输出

25713864
17582463
36824175
#include<cstdio>
#include<stdlib.h>
int count=0;
int n,a;
int P[10],hashTable[10]= {false};
void generateP(int index)
{
    if(index==8+1)//递归边界,生成一个排列
    {
        count++;//能到达这里一定是合法的
        if(count==a)
        {
            for(int i=1; i<=8; i++)
                printf("%d",P[i]);
            printf("\n");
        }


        return;
    }
    for(int x=1; x<=8; x++)//第x行
    {
        if(hashTable[x]==false)//第x行还没有皇后
        {
            bool flag=true;//flag为true表示当前皇后不会和之前的皇后冲突
            for(int pre=1; pre<index; pre++) //遍历之前的皇后
            {
                //第index列皇后的行号为x,第pre列皇后的行号为P[pre]
                if(abs(index-pre)==abs(x-P[pre]))
                {
                    flag=false;//与之前的皇后在一条对角线,冲突
                    break;
                }
            }
            if(flag)//如果可以把皇后放在第x行
            {
                P[index]=x;//令第index列皇后的行号为x
                hashTable[x]=true;//第x行已经被占用
                generateP(index+1);//递归处理第index+1行皇后
                hashTable[x]=false;//递归完毕,还原第x行为未占用
            }
        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&a);
        generateP(1);
        //printf("%d皇后可行方案:%d种\n",n,count);
        count=0;
    }
    return 0;
}

题目来自:http://codeup.cn/problem.php?cid=100000583&pid=3

 

补充:在论坛上看到有人写出了更快更巧的方法,感觉很是高深,值得研究一番(可怕的是没有注释。。)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
long sum = 0, upperlim = 1;
 
void test(long row, long ld, long rd)
{
 
    if (row != upperlim)
    {
        long pos = upperlim & ~(row | ld | rd);
        while (pos)
        {
            long p = pos & -pos;
 
            pos -= p;
            test(row + p, (ld + p) << 1, (rd + p) >> 1);
        }
    }
    else
        sum++;
}
 
int main(int argc, char *argv[])
{
    time_t tm;
    int n = 16;
 
    if (argc != 1)
        n = atoi(argv[1]);
    tm = time(0);
    if ((n < 1) || (n > 32))
    {
        printf(" 只能计算1-32之间\n");
        exit(-1);
    }
    printf("%d 皇后\n", n);
    upperlim = (upperlim << n) - 1;
 
    test(0, 0, 0);
    printf("共有%ld种排列, 计算时间%d秒 \n", sum, (int) (time(0) - tm));
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值