这个是很经典的问题了!不详细介绍了。我会放上题目和代码,以及可能整个CSDN独有的“人话翻译”!(取n=4的情况详细讲解,全是婴语,因为我本人都还在看着专业术语会头大的阶段)
先感谢其它的优质文章,我是因为学习了这些文章才知道该怎么做这道题,孩子是递归初学者捏!
非常非常推荐大家在不清楚具体过程的时候去debug一下。我刚学会最最基础的,在scanf后面一句打一个断点,然后就不停地点逐语句,并且在草稿纸上画出4×4的格子,随着监视变量的变化对着格子比划,其实真的不难!debug就像无言的老师,会告诉你计算机是怎么“想”的。
题目:
8皇后问题是在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
现在请你求在n*n的棋盘上摆放n个皇后的方案数。
输入格式
一行,一个正整数n ( n < 13)
输出格式
一行,一个整数,表示所求的方案数
输入样例
8
输出样例
92
代码(已ac):
#include<stdio.h>
int attack(int row, int col);
void findspace(int row, int n);
//row既是当前行数,也是正在找地方的queen的序号
int queen[13] = { 0 };
int solution = 0;
int main(void) {
int n;
scanf("%d", &n);
findspace(0, n);
printf("%d", solution);
return 0;
}
int attack(int row, int col) {
for (int i = 0; i < row; i++) {
if (col == queen[i])
return 1;
if (row - i == queen[i] - col || row - i == col - queen[i])
return 1;
}
return 0;
}
void findspace(int row, int n) {
if (row >= n) { //如果n个已经放完了
solution++;
return;
}
for (int col = 0; col < n; col++) {
if (attack(row, col) == 0) {
//第row行的第i个位置不会被攻击,把queen[row]放进去
queen[row] = col;
findspace(row + 1, n);//没放完,找下一个queen的位置
}
}
}
整体思路解释:
所有坐标和循环计数用的变量都从0开始计,人话数数我会从一开始数,n也是人话数数,n=4的时候row和col的变化范围都是0,1,2,3。并且后文为了防止混淆,从1开始数数的我都写成汉语数字。
每一行放一个皇后,queen[ row ] = col 的意思就是第row行的那个皇后(也就是第row个皇后)被放在了第col列(也就是第row行的第col个位置)。
比如queen[ 2 ] = 3 翻译成人话就是 “把第三个皇后放在棋盘第四列”。
用attack(row,col)来确定(row,col)坐标的位置是否被攻击。
如果attack函数返回1,那就是会被攻击;如果attack函数返回0,那就是不会被攻击。
它的思路:通过一个for循环检查从0到row-1的这row个皇后会不会攻击(row,col)这个位置。
i是用到的循环变量,比如我们确定attack(2,1)返回0还是1,那么i的范围就是0和1。
注意(2,1)是第三行第二列!
i=0的这一轮循环负责检查第一行的皇后会不会攻击(2,1)
i=1的这一轮循环负责检查第二行的皇后会不会攻击(2,1)
那么具体怎么看会不会攻击呢?检查是不是在同一列、是不是在同一对角线。在同一对角线 的意思就是行指标的间距等于列指标的间距,也可以用math库里面的fabs()取绝对值。
用findspace函数为皇后找到放置的位置。
因为每一次调用这个函数都是为一个皇后找位置,所以传入参数只需要row和n。row用来确定我这次在给谁找位置,n用来确定我最远能找到这一行的第几个位置(col不能超过n就是for循环的第二个条件)
每次开始找位置的时候都要检查一下进度:我找到第几个皇后啦?如果已经找完了,那就把解法++,然后return。return去哪了呢,就是返回上一个调用它的函数。(这个在后文的debug解释会展开说)
如果进度还在中间,比如我们有四个皇后结果才找到第二个呢,那就从for循环开始执行:从col=0一直到col=n-1,循环n次,一个一个问,attack等于0还是1呀(我呆在这会被揍吗?)而一旦找到一个地方attack返回0(不被攻击),那么恭喜你!if满足了,可以进行赋值语句:queen[row]=col了(这个皇后找到一个位置,已经安顿好了),并且递归:第row个找到啦!快去按同样的方法(递归是调用自己,是同一个函数,当然就是同样的方法)找第row+1个吧。
我最开始不懂的点在于,那回溯呢?回溯体现在哪里,怎么告诉我的电脑,你要是找不到的话就回头重新找第row-1个?是不是还要写类似下面这种东西?
else{ findspace(row-1, n) }
其实不需要!这个就要结合debug的过程细说了。
debug详细解释
从这里开始黑字是我在(用代码)和电脑说话,蓝字是我在说话,紫字是电脑在跟我说话。
#include<stdio.h>
int attack(int row, int col);
void findspace(int row, int n);
给你一个头,给你声明两个函数,我待会儿要用!
int queen[13] = { 0 };
int solution = 0;
我最多找13个皇后的情况,我要找解法的种数,现在让它是0。
int main(void) {
int n;
scanf("%d", &n);
findspace(0, n);
printf("%d", solution);
return 0;
}
看!这是你要做的任务概览。我给你一个n=4,你给四个皇后们找位置,找完了告诉我解法有几种(solution=?)
我怎么找捏?
马上教你!
int attack(int row, int col) {
for (int i = 0; i < row; i++) {
if (col == queen[i])
return 1;
我现在还不知道她在干什么?不过在同一列的皇后会被攻击对吧,我知道了,一会儿要用到的时候她会叫我调用这个函数的。
if (row - i == queen[i] - col || row - i == col - queen[i])
return 1;
同一斜线上的皇后也会被攻击啊。
}
return 0;
}
如果两个if都不满足,就不会被攻击了捏。
void findspace(int row, int n) {
if (row >= n) {
solution++;
return;
}
你记得每次回来的时候先检查一下四个皇后找完没有!要是找完了就把解法加一,然后回到上一次调用你的函数。
也就是如果现在的你是findspace(4, 4)那就说明找完了,你就要return到findspace(3, 4)那里去探索下一种解法。(注意一下后面那个4代表的是n,整个下文中它都是4不会变)
听不太懂捏……我才刚来,我现在是findspace(0, 4), 这个奇怪的if先跳过吧。
接下来我要循环很多次了。不然我用一下编号记录一下现在是第几周目吧?免得把自己绕晕了。
画外音:比如findspace(0, 4)中的col=1就记作 周目0.1!而且大家记得画一个4×4的格子跟着一起看!
for (int col = 0; col < n; col++) {
if (attack(row, col) == 0) {
queen[row] = col;
findspace(row + 1, n);
}
}
}
周目0.0!(给第一个皇后找位置,在试第一列)
调用了一下attack,肯定返回0啦。好!赋值。现在第一个queen在第一列住下了。
周目1.0!(给第二个皇后找位置,在试第一列)
调用一下attack(1, 0),i=0的时候就返回1了,说明这个坐标会被第一个皇后攻击诶。
周目1.1!(给第二个皇后找位置,在试第二列)
调用一下attack(1, 1),还是返回1。
周目1.2!(给第二个皇后找位置,在试第三列)
这次返回0了,好,赋值,找下一个queen的位置!
周目2.0!周目2.1!周目2.2!周目2.3!
坏了,第三个皇后怎么都不行啊,我还能再试吗,(回去检查一下for的那一行)哦,col<4,这样就已经加到4了,我没机会了。看来这是条死路,前面的摆法不对,我得退回去重新来。
(慌张)等下,我没写,我不知道你怎么回溯,需要我帮忙吗(瑟瑟发抖)
没事啦我会自己回去的!这一轮我四次if都不满足,相当于这一轮我什么都没干。哪一个都不符合不就是被跳过了吗?如果我这轮找到了第三个皇后的位置,我会继续调用自己去找第四个的,可是我没找到,我没办法调用下一个自己了,而且我所有的执行步骤都做完了,也就是我会回到上次调用我的那个函数。findspace(1, 4)是在col还没循环完的时候调用我的,现在它可以继续做它没做完的尝试了。
哦!确实,findspace(1, 4)把第二个皇后放在第三列之后就把你调出来了,可是它还没试第四列呢(它还剩一个周目1.3!可以探索)。你被调出来了并且啥都没干就结束了,所以它就可以仿佛无视发生,继续它的进度了。
周目1.3!
诶不错,这个地方也不会被攻击。刚刚把第二个皇后放在第三列了走不通,这次把它放在第四列。我再去试试看第三个皇后能不能找到!(召唤findspace(1, 4))
快进到所有的都执行完返回完了,findspace(0, 4)在风中凌乱
原来把第一个皇后放在第一列虽然不会被攻击,但是后面走不通。好吧,我也才经历过周目0.0而已,现在回到了这里,后面的函数都啥也没干(都不符合解法所以啥也没干就返回了),那我就当无事发生,接着我0.0的进度往后走吧。
周目0.1!
现在我把第一个皇后放在第二列了!你们再试试看!(召唤findspace(1, 4))
快进到成功调出了findspace(3, 4)
周目3.2!
成功了,第四个皇后被我放到了第三列!
(不好意思治一下大家颈椎?才用CSDN不会旋转图片 图片对应这一种成功的情况)
这时电脑并不知道自己放完了,它还会调出findspace(4, 4)再判断一下,哦满足if条件了,这才把solution++并且return,这时候就是回到findspace(3, 4),继续它的周目3.3!
感觉以下可以省略了,说起来本来没打算说的这么幼儿园画风的,不知不觉……期间也怀疑,我是不是太啰嗦啦?讲这么细。不过我相信肯定有当初和我一样云里雾里晕晕乎乎弄不懂递归的人在。这篇文章就是给小白准备的,肯定肯定有不会的人,希望如果真的有帮到你的话,在底下给我留个评论!!(不然我会觉得没人看这个,自己像个白痴orz)