从算法学起C语言--八皇后(普通算法)

转载请注明出处,谢谢~

背景说明:

西洋棋中的皇后可以直线,斜线吃掉所遇到的棋子,如果在8*8的棋盘上有八个皇后,则这八个皇后如何相安无事的摆放在棋盘上?1970年与1971年,E.W.Dijkstra与N.Wirth曾经用这个问题来讲解程式设计之技巧。

大天朝白话文:

有8*8的棋盘,需要在这个棋盘上摆放8个皇后(相同横、竖、斜线上不能再有皇后),求一种有多少种不同 的摆法?


图片一摆,很容易想到二维数组解决问题,就是在遍历的过程中判断二维数组的值是不是等于我们设定的此处可以摆放皇后的值。但是这样光想想就知道代码会略微繁琐,因为我们每放置一个皇后就要更改同行同列同斜线上的值,那么有没有一种方式可以不需要改变如此多的值就能解决问题?找规律!

假设我们从第一行开始摆放皇后,也就是图中的最后一行,我们会发现我们并不需要判断哪一行可以放置皇后,因为我们会递归的以行为基数进行循环,第一行放完了当然就是第二行,在第二行我们只需要判断第二行中的哪一列能放置就行,同样在第三行只考虑哪一列能放置皇后,所以这就省去了行的判断,我们用一个一位数组表示列定义为column[ ],然后我们再来考虑斜线。

反斜线:


规律显而易见,一条斜线上的所有可摆放位置的坐标和值是相等的!即x+y=i+j;那么这条斜线上能不能放置皇后就是用一个和值就能判断,而不需要二维数组来更改值,我们用一个一维数组保存和值,和值为一维数组的下标。即rup【i+j】就是rup【1+8】,那么如果这个数组中的值不是可以放置皇后的值,就说明这条斜线上就不能放置皇后了。


正斜线:


同样规律显而易见,但是这里有个问题,会出现负值。有人说取绝对值就可以了,但是同样的绝对值也代表了另一条斜线,所以这个规律暂时舍弃,看看有么有其他的规律。


只要在后边加上一个8就可以,这样就保证值的正数和唯一性。然后同样定义一个数组lup表示正斜线,值存在对应的数组下标中。然后我们就可以构造代码了。

void backtrack(int i)
{
	int j;
	if(i > N)
		showAnswer();
	else
		for (j=1;j<=N;j++)
		{
			if(column[j] == 1 && rup[i+j] == 1 &&lup[i-j+N] == 1){
				queen[i] = j;
				//设定为占用
				column[j] = rup[i+j] = lup[i-j+N] = 0;
				backtrack(i+1);
				column[j] = rup[i+j] = lup[i-j+N] = 1; //在左下角递归完后进行下一个位置的递归运算                                                                                       
			}
		}
}

这样,通过三个一维数组就保证了不会出现冲突的皇后。同样,上述代码中,我们用一个queen数组存放每一行的位置上,皇后放置在了哪一列,列数就是数组对应的值。下面附上完整代码:

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

int column[N+1];//同栏是否有皇后
int rup[2*N +1];//右上至左下是否有皇后
int lup[2*N +1];//左上至右下是否有皇后
int queen[N+1] = {0};
int num ;//解答编号

void backtrack(int);//递回求解

main(void)
{
	int i;
	num = 0;
	for(i=1;i<=N;i++)
		column[i] = 1;

	for(i=1;i<=2*N;i++)
		rup[i] = lup[i] = 1;

	backtrack(1);

	system("pause");
	return 0;
}

void showAnswer()
{
	int x,y;
	printf("\n 解答 %d\n",++num);
	for (y=1;y<=N;y++)
	{
		for (x=1;x<=N;x++)
		{
			if(queen[y] == x)
				printf("Q");
			else
				printf("K");
		}
		printf("\n");
	}
}

void backtrack(int i)
{
	int j;
	if(i > N)
		showAnswer();
	else
		for (j=1;j<=N;j++)
		{
			if(column[j] == 1 && rup[i+j] == 1 &&lup[i-j+N] == 1){
				queen[i] = j;
				//设定为占用
				column[j] = rup[i+j] = lup[i-j+N] = 0;
				backtrack(i+1);
				column[j] = rup[i+j] = lup[i-j+N] = 1; //在左下角递归完后进行下一个位置的递归运算                                                                                       
			}
		}
}

运行下:


可以看到一共有92种结果,完美执行~!

  • 30
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值