八皇后问题。。。。。。

来源:互联网 发布:linux cpu热插拔 编辑:程序博客网 时间:2024/06/05 00:13

 HIT ACM 2009 Summer Contest 03  A : (八皇后问题)解题报告
-----------------------------------------------------------------
题目大意:给定一个 size * size (size作为输入)的棋盘,要求在棋盘上放size个棋子,要求每一行、每一列以及每一斜行只能有一个棋子,要求输出这样放能有多少种放法以及第一种放法;

分析:题目的关键 就在于确定某个位置是不是可以放一个棋子,而只有在该位置的同一行、同一列以及两个斜行都没有其他棋子时这个位置就是合法的,因为每一行只有一个棋子,所以不需要一个 size * size 的数组来存结果,只需 pos[] 来存就行, pos[j] = i 就表示在第j行 第i列处有一个棋子,而这样就可以保证每一行只有一个棋子了,所以在检查的时候就不需要检查行了,检查列也可以用一个数组 col[] 标记,当col[i] = 1 时证明第i列已有棋子;而对于斜行:

如下图,给各个斜行标号(一 size = 5 为例):

主对角:
                 
            5 4 3 2 1
            6 5 4 3 2
            7 6 5 4 3
            8 7 6 5 4
            9 8 7 6 5    则可以发现规律:主对角方向上的 斜行 上的位置有:num 为斜行的编号 则: num = size - ( row - column )
                         那么现在又只需要一个一维数组来标记主对角方向的斜行; dia[]

反对角:    1 2 3 4 5
            2 3 4 5 6
            3 4 5 6 7
            4 5 6 7 8
            5 6 7 8 9   则同样可以发现规律: num = row + column - 1  同样一个一维数组可以满足要求;bdia[]

这样,一个位置合法的条件就出来了  位置为 ( r,c )   即: col[c] == 0 && dia[size - (r - c)] == 0 && bdia[r + c - 1] == 0
可以写一个函数 legal() 来检查:

int legal(int r,int c)
{
    if (col[c] == 1)
        return 0;
    if (dia[size - (r - c)] == 1)
        return 0;
    if (bdia[r + c -1] == 1)
        return 0;
    return 1;
}                                

------------------
这样知道位置是否是合法的后,就好办了:
         从第一行开始每行进行 深搜, 找合法的位置,如果找到 标记 (注意:三个数组都要标记),就从递归调用从下一行继续开始深搜:


void DFS(int c)
{
    int i;
    for (i = 1;i <= n;i++)
    {
        if (legal(i,c))
        {
            ocp(i,c);  //标记这个位置
            if (c < n)
                DFS(c+1);  //递归调用
            else
            {
                count++;
                display();  //显示结果
            }
            unocp(i,c);  //释放这个位置
        }
    }
}
-------------------------
这样这个问题就解决了…………
--------------------------------------------------------
思考:自己写好了交上去以后, 一个 TLE 让我无语, 后来由于在比赛中,  呵呵  数据 比较小  1 =< size <= 13   所以  我就把 13  打表过了   鄙视自己一下…………

下来后, 落花神牛 讲了一个优化的方法: 考虑棋盘的对称性  即: 如果 第一行的 第 i 个位置有一颗棋子  那么在第一行的倒数第i个位置有一颗棋子同样对应了另一组解……  呵呵  没想到啊
   那么考虑 size = 2 * k - 1   即奇数的时候   对第一行的列进行搜索,对以后每一行的每个位置搜索,而第一行只需搜到最中间一列的前一列为止,记解数为 T ,在单独考虑 第一行中间有一颗棋子的情况,注意,棋盘也是上下对称的  所以 在棋盘最后一行如果中间有棋子的话,记录 次情况下的解数 B ,则总的结果即为 2 * T + B;
   当size为偶数时就更简单了:即为 2 * T ;
很巧啊,减少了一半的搜索量……
----------------------------------------------------------------------
    from : Debug cool     
           7.25