N皇后问题的递归回溯和非递归非回溯方法(源码)c语言

1 篇文章 0 订阅
1 篇文章 0 订阅

本人大三、计算机专业,算法课期末设计要求对N皇后问题进行求解。对于N皇后这个经典问题,最简洁的方法一定是递归回溯。写作业时突然脑洞大开,不使用递归、不使用回溯,我们能否解决N皇后这个经典难题?

答案一定是可以的,经过两天的奋战,最终仅仅借助一个栈就实现了对N皇后问题的非递归非回溯解法。下面将分享递归回溯和非递归回溯两种解决N皇后问题的思路。

问题重述:N问题是十九世纪著名的数学家高斯于1850年提出的。问题是:在n×n的棋盘上摆放n个皇后,使任意两个皇后都不能处于同一行、同一列或同一斜线上。 


1,经典递归回溯法:

基本思路:定义一个检查(check)函数,从第一行第一列元素开始,查看该位置是否可以放置皇后。检查方法是对这个元素所在行(i==x),所在列(j==y),所在对角线(i+j==x+y&&i-j==x-y)进行遍历,查看是否已经存在皇后。

定义搜索函数:从第一行第一列开始搜索,若可以放置,则递归进入下一行;若不满足放置条件,回溯到上一行。

直接上代码(宏定义的N是皇后数)

#include <stdio.h>
#include <stdlib.h>
#define N 6
int ans = 0;
int check(int pd[N][N], int x,int y)  //检查函数
{

    for(int i=0;i<N;i++)
    {
        if(pd[i][y]==1)
        {
            return 0;
        }
    }
    for(int i=0;i<N;i++)
    {
        for(int m=0;m<N;m++)
        {
            if(i-m==x-y&&pd[i][m]==1) 
            {
                return 0;
            }
        }
    }
    for(int i=0;i<N;i++)
    {
        for(int m=0;m<N;m++)
        {
            if(m+i==x+y&&pd[i][m]==1)
            {
                return 0;
            }
        }
    }
    return 1;
}
void printf_T(int T[N][N]) //打印输出二维数组函数
{
     for(int i=0;i<N;i++)
    {
        for(int m=0;m<N;m++)
        {
            printf("%d",T[i][m]);
            if(m==N-1)
            {
                printf("\n");
            }
        }
    }
    printf("\n");
}
int dfs(int pd[N][N], int num) //搜索函数
{

    if(num == N)
    {
        ans=ans+1;
         printf("\nµÚ%d¸ö½á¹û\n",ans);
        printf_T(pd);


    }
    for(int i=0;i<N;i++)
    {
        if(check(pd,num,i))
        {
            pd[num][i] = 1;
            dfs(pd,num+1);  //递归进入下一行
            pd[num][i] = 0;
        }
    }

}
int main()
{
    int pd[N][N] = {0};
    dfs(pd,0); //将第1行放入递归入口
    return 0;
}

2,非递归非回溯法:

在国际象棋中,皇后能够杀死跟他同一列,同一行,同一对角线的所有棋子。借助这个特性,我们实现了非递归非回溯解决N皇后问题。

基本思路:简单的来说就是从第一行开始,放置第一个皇后,将棋盘上皇后能碰到的地方(所在行,所在列,所在对角线)全部杀掉。进入下一行,如果下一行有可以放置皇后的位置,继续放皇后,继续杀。如果没有可以放皇后的位置,给刚刚杀死的位置复活,标记上一个皇后的位置,回到上一行。如果杀到最后一行仍然有可以放置皇后的位置,answer+1,打印输出棋盘,接着复活最后一个皇后杀死的位置,回到上一行。

借助一个长度为N的栈,满足放置条件:先入栈,将该皇后能够到的位置全部杀死,继续找下一行;若下一行找不到能放置皇后的位置:栈头元素出栈,复活该皇后杀死的位置,将出栈皇后位置标记(借助辅助数组),下次遍历不再访问该皇后。如果杀到最后一行,仍然有存活的位置,打印输出棋盘。

源码:(N为皇后个数)

#include <stdio.h>
#include <stdlib.h>
#define N 7

typedef struct //栈
{
    int data[N];
    int top;
}SqStack;

void InitSqStack(SqStack *S) //初始化栈
{
    //S = (SqStack*)malloc(sizeof(SqStack));
    S->top=-1;
}

int Push(SqStack *S,int m,int n)//进栈
{

    if(S->top == N-1)
    {
        return 0;
    }

    S->top++;

    S->data[S->top] = m*10+n;


    return 0;
}
int pop(SqStack *S ,int *x,int *y)//出栈
{
    if(S->top == -1)
    {
        return 0;
    }

    *x = S->data[S->top]/10;
    *y = S->data[S->top]%10;
    S->top--;
    return 1;

}
int StackEmpty(SqStack *S)//判断栈是否为空
{
    if(S->top == -1)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

int turn_1(int T[N][N],int x,int y) //杀!
{
    for(int i=0;i<N;i++)
    {
        for(int m=0;m<N;m++)
        {
            if(i==x&&m!=y)
            {
                T[i][m]++;
            }
            if(m==y&&i!=x)
            {
                T[i][m]++;
            }
            if(m+i==x+y&&i!=x)
            {
                T[i][m]++;
            }
            if(i-m==x-y&&i!=x)
            {
                T[i][m]++;
            }
        }
    }
return T;
}
int turn_0(int T[N][N],int x,int y) //复活
{

    for(int i=0;i<N;i++)
    {
        for(int m=0;m<N;m++)
        {
            if(i==x&&m!=y)
            {
                T[i][m]--;
            }
            if(m==y&&i!=x)
            {
                T[i][m]--;
            }
            if(m+i==x+y&&i!=x)
            {
                T[i][m]--;
            }
            if(i-m==x-y&&i!=x)
            {
                T[i][m]--;
            }
        }
    }
return T;

}
int find_0(int D[N],int D2[N]) //查找是否有可以放置皇后的位置
{
    for(int i=0;i<N;i++)
    {
        if(D[i]==0&&D2[i]==0)
        {
            return 1;
        }
    }
    return 0;
}
void printf_T(int T[N][N]) //格式化、打印棋盘
{
     for(int i=0;i<N;i++)
    {
        for(int m=0;m<N;m++)
        {
            if(T[i][m]==0)
            {
                printf("1");
            }
            else
            {
                printf("0");
            }
            if(m==N-1)
            {
                printf("\n");
            }
        }
    }
    printf("\n");
}

int main()

{
    int ans=0;


    for(int w=0;w<N;w++)
    {
    SqStack sq;
    InitSqStack(&sq);
    //int res = find_0(td[0]);
    int m=0;
    int x;
    int y;
    int td[N][N]={0};//棋盘
    int index[N][N]={0}; //辅助棋盘
    turn_1(td,0,w);
    Push(&sq,0,w);

    while(sq.top!=-1)
    {
        if(m!=N-2&&find_0(td[m+1],index[m+1]))  //如果下一行存在可以放置皇后的位置
        {
            for(int i=0;i<N;i++)
            {

                if(td[m+1][i]==0&&index[m+1][i]==0)
                {

                    Push(&sq,m+1,i);
                    //printf("ces\n");
                    turn_1(td,m+1,i);
                    //printf_T(td);
                    m=m+1;
                    break;
                }
            }
        }
        else if(m==N-2&&find_0(td[N-1],index[N-1])) //查找到最后一行,打印输出,继续回到上一行
        {


                ans++;
                printf("µÚ%d¸ö´ð°¸\n",ans);
                printf_T(td);
                for(int j=0;j<N;j++)
                {
                    index[m+1][j]=0;
                }
                pop(&sq,&x,&y);
                index[x][y]=1;
                turn_0(td,x,y);

                //td[x][y]++;
            //printf_T(td);

                m--;



        }
        else    //没找到,回到上一行
        {
            for(int j=0;j<N;j++)
            {
                index[m+1][j]=0;
            }
            //td[x][y]--;
            //printf("pop\n");
            pop(&sq,&x,&y);
            index[x][y]=1;
            turn_0(td,x,y);
            //printf_T(td);
            m--;

        }
    }}
    return 0;
}

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值