本人大三、计算机专业,算法课期末设计要求对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;
}