八皇后问题

问题描述

国际象棋中的“皇后”可以吃掉与它在同一行、同一列、同一对角线上的棋子。八皇后问题,即是在 8 行 8 列的国际象棋棋盘上放置 8 个皇后,并使它们互相不会攻击。

解决方法

8 个皇后互相不攻击,其实也就是要使得整个棋盘上任一行、任一列、任一对角线上只能有一个皇后存在。通常我们采用回溯法来解决。 我们使用一个顺序存储的栈,栈中结点有三个字段,其形式为 (row, col, tag),其中 row 和 col 分别表示棋盘上的行号与列号,表示棋子在棋盘上的位置,而 tag 的值取 0 或 1。当新结点进栈时,它的 tag 值取 0,当栈顶结点被判断为可能成为布局中的一个位置时,则把栈顶结点的 tag 值置成 1,并产生它的 n 个子结点,让这 n 个子结点进栈,同时置这 n 个子结点的 tag 值为 0。用这样的顺序栈存放所有有待进一步处理的解答树的子树的根结点。栈中还存放着从根结点向下通往正在考虑的结点的那条树枝上的结点。设 k 是栈顶上的结点,它的值为 (r, c, o),在往栈中添加 k 的 n 个子结点之前,必须检查 k 所代表的皇后是否在攻击别的皇后,即在 c 列,或在通过 (r, c) 位置的、编号为 (r – c + n – 1) 的主对角线上,或通过 (r, c) 位置的,编号为 (r + c) 的次对角线上是否已经有皇后。如果我们用 col[2],md[2 * n – 1] 和 sd[2 * n – 1] 这三个数组,那么我们就能很容易地查清这个问题(用 0 和 1 做标志来表示此行、列、对角线上是否已经有皇后)。

代码

源代码如下,也可以 单击此处下载源代码
#define MAXN 16

/* 定义栈结点,表示一个皇后的位置 */
typedef struct node
{
    int row; /* 行 */
    int col; /* 列 */
    int tag; /* 布局标记 */
} NODE;

/* 进行皇后问题处理
 * 参数 n 表示棋盘的大小
 * 返回找到的答案个数
 */
int queens(int n)
{
    /* 定义解答树堆栈 */
    NODE stack[MAXN * MAXN];
    /* 栈顶索引,初始化为 -1(栈空) */
    int top = -1;
    /* 攻击标志,表示同一列及对角线上是否有皇后 */
    int col[MAXN] = {0},
        md[2 * MAXN - 1] = {0},
        sd[2 * MAXN - 1] = {0};
    int str, stc, i;
    /* 解决方案个数 */
    int scount = 0;

    /* 初始化栈 */
    for(i = 0; i < n; i++)
    {
        stack[++top].row = 0;
        stack[top].col = n - 1 - i;
        stack[top].tag = 0;
    }

    /* 以行为单位开始回溯 */
    while(top >= 0)
    {
        str = stack[top].row;
        stc = stack[top].col;

        if(stack[top].tag == 0)
        {
            /* 如果栈顶元素的位置并没有确立 */
            if(col[stc] || md[str - stc + n - 1] || sd[str + stc])
            {
                /* 如果同一列或同一对角线上已有皇后,则退回 */
                top--;
            }
            else
            {
                /* 占据这个位置,设置列、对角线上的攻击标志 */
                col[stc] = 1;
                md[str - stc + n - 1] = 1;
                sd[str + stc] = 1;
                /* 标记栈顶元素的 tag 值 */
                stack[top].tag = 1;

                if(str == n - 1)
                {
                    /* 如果此时已经到达最后一行
                        * 则表示此种布局方法是成功的
                        * 输出相关信息
                        */ 
                    printf("A solution is:/n");
                    for(i = 0; i <= top; i++)
                    {
                        if(stack[i].tag)
                            printf("%4d,%3d;", stack[i].row, stack[i].col);
                    }
                    printf("/n");
                    /* 解决方案数增 1 */
                    scount++;
                }
                else
                {
                    /* 如果此时没有到达最后一行
                        * 则继续进栈并初始化
                        */
                    for(i = 0; i < n; i++)
                    {
                        stack[++top].row = str + 1;
                        stack[top].col = n - 1 - i;
                        stack[top].tag = 0;
                    }
                }
            }
        }
        else
        {
            /* 如果栈顶元素位置已确立
               * 则栈顶元素出栈,初始化攻击标志
               * 准备继续寻找其它的方法
               */
            col[stc] = 0;
            md[str - stc + n - 1] = 0;
            sd[str + stc] = 0;
            top--;
        }
    }

    return(scount);
}

void main()
{
    int n;
    int scount = 0;

    printf("/nInput n (n <= 15) = ");
    scanf("%d", &n);

    if(n > MAXN - 1)
    {
        printf("### n > %d ###/n", MAXN - 1);
        exit(1);
    }

    scount = queens(n);
    printf("%d sulotions found./n", scount);

    getch();
}		
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值