关于使用啰嗦代码瞎混到ac的n皇后问题
-
题目地址:ACwing-n皇后
-
关于思路:
- 题目概述:n−皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数 n,请你输出所有的满足条件的棋子摆法。 - 首先了解题意:
1.n×n 的国际象棋棋盘上,可以使用一个二维数组来表示棋盘方阵。二维数组中每个元素的两个下标来表示该元素在棋盘上的坐标(x,y)。
2.关键词在于“任意两个皇后都不能处于同一行、同一列或同一斜线上”,即每次落子(摆放皇后),落子坐标(x,y)要满足“不与之前已摆放的皇后坐标处于同一行、同一列或同一斜线上”的条件
换句话说“每一行每一列每一个斜线上只能有一颗唯一的棋子”。 - 思路: 先解决判定落子坐标的合法性问题,再使用“行行递进”的方式选定每行的落子坐标。
问题1.判定落子坐标的合法性:使用四个数组x[],y[],k[],l[](初始=0)来记录各行各列各主斜线各副斜线是否已经有子(有子=1),此时给定第一个落子坐标(a,b)(任意坐标必然合法),
落子完成,此时对四个数组进行更新x[a]=1,y[b]=1,k[x+y]=1,l[x-y]=1。再给定第2--n个落子(i,j),若x[i]=0,y[j]=0,k[i+j]=0,l[i-j]=0,说明该落子坐标满足条件“与其他落子不处于同 一行、同一列或同一斜线上”,此时即可落子完成,再次对四个数组更新······
问题2.使用“行行递进”的方式选定每行的落子坐标:已知“每行必然有且只有一个落子”首先可以自定义第一行(即第一个落子的坐标),第一行有子后,进入第二行,进行遍历,直到找到合法坐标,再进入下一行······当进入至第n行(即最后一行),并成功落子,此时解法成立,将其print出来。
- 题目概述:n−皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
-
关于啰嗦代码:
初试代码
#include<stdio.h>
int x[33],y[33],k[33],l[33],n;
int a[111][111];
int check(int xx,int yy) //判定落子坐标是否合法
{
if(x[xx]||y[yy]||k[xx-yy]||l[xx+yy])
{return 1;}
else
{return 0;}
}
void reset(int xx,int yy) //重置坐标
{
a[xx][yy]=0;
x[xx]=0;
y[yy]=0;
k[xx-yy]=0;
l[xx+yy]=0;
}
void create(int xx,int yy) //创建坐标
{
a[xx][yy]=1;
x[xx]=1;
y[yy]=1;
k[xx-yy]=1;
l[xx+yy]=1;
}
int search(int s) //递归遍历
{
int f=0;
for (int w=1;w<=n;w++)
{
if (check(s,w)==0)
{
create(s,w);
if (s==n)
{return 1;}
if (search(s+1))
{
return 1;
}
else
{
reset(s,w);
return 0;
}
f=1;
}
}
if (f==1)
{return 1;}
else
{return 0;}
}
int main()
{
scanf("%d",&n);
int f=0;
if (n==1)
{
printf("Q");
return 0;
}
for (int q=1;q<=n;q++)
{
//初始化状态
f=0;
for (int qq=0;qq<=20;qq++)
{
for (int ww=0;ww<=20;ww++)
{a[qq][ww]=0;}
}
for (int qq=0;qq<=32;qq++)
{
x[qq]=0;y[qq]=0;k[qq]=0;l[qq]=0;
}
create(1,q);
if (search(2))
{
f=1;
for (int i=1;i<=n;i++)
{
for (int w=1;w<=n;w++)
{
if (a[i][w])
{
printf("Q");
}
else
{
printf(".");
}
}
printf("\n");
}
}
reset(1,q);
if (f)
{printf("\n");}
}
}
初试代码AC失败,错误在于,递归的方式没有考虑到一行拥有多个分支的情况,只是一条路走到底,以至于最后print出的解法与答案相比还有缺失
最终版
#include<stdio.h>
int x[33],y[33],k[33],l[33],n;
int a[111][111];
int check(int xx,int yy)
{
if(x[xx]||y[yy]||k[xx-yy]||l[xx+yy])
{return 1;}
else
{return 0;}
}
void reset(int xx,int yy)
{
a[xx][yy]=0;
x[xx]=0;
y[yy]=0;
k[xx-yy]=0;
l[xx+yy]=0;
}
void prl()
{
for (int i=1;i<=n;i++)
{
for (int w=1;w<=n;w++)
{
if (a[i][w])
{
printf("Q");
}
else
{
printf(".");
}
}
printf("\n");
}
}
void create(int xx,int yy)
{
a[xx][yy]=1;
x[xx]=1;
y[yy]=1;
k[xx-yy]=1;
l[xx+yy]=1;
}
int search(int s)
{
int f=0,ff=0;
for (int w=1;w<=n;w++)
{
if (check(s,w)==0)
{
create(s,w);
if (s==n)
{
prl();
f=1;
}
if (search(s+1))
{
f=1;
}
else
{
reset(s,w);
f=0;
}
}
}
return f;
}
int main()
{
scanf("%d",&n);
int f=0;
if (n==1)
{
printf("Q");
return 0;
}
for (int q=1;q<=n;q++)
{
//初始化状态
f=0;
for (int qq=0;qq<=20;qq++)
{
for (int ww=0;ww<=20;ww++)
{a[qq][ww]=0;}
}
for (int qq=0;qq<=32;qq++)
{
x[qq]=0;y[qq]=0;k[qq]=0;l[qq]=0;
}
create(1,q);
if (search(2))
{
f=1;
}
reset(1,q);
if (f)
{printf("\n");}
}
}
相比与初试代码,主要在递归函数里做了一些改动,使其可以遍历一整行的所有可能,给出的解法更完全
有待改善:相比于题库中给出的其他题解,这套代码显得十分臃肿啰嗦,应该系统性的学习一下相关的算法知识,以更简洁更规范的方式解答