从汉诺塔到八皇后问题

汉诺塔题目:

在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。问:若金片(盘子)只有N个(用户输入),至少移动多少次,且如何移动,才能将金片全部移动到另外一根针上?

归根到底还是递归嘛;

汉诺塔的思路是这样的:

{

如果只有两个盘子:

把一号盘子(所有盘子从上到下依次编号)从A到B

二号放入C,一号再移入C

如果有三个盘子:

把除了最下面(3号的盘子)移入B,(这里我们发现有两个盘子移入B,如何做?只要把他又看成一个独立的过程,就可以发现,我们又回到了只有两个盘子,有些同学可能会问,我们这是移动到B啊,上一个是移动到C啊,可是我们把B和C换过来看,把C看成暂存盘,B看作目标盘,就可以发现,其实是一样的,特别是编程中,我们只需要交换两个参数位置就可以实现)

最下面的移入C,B中的所有盘子再移入C;(这时注意,B中所有盘子移入C是不是很奇怪,他有两个盘子在上面(1和2号),所以,我们又可以把他当作从A移动到C的过程,尽管他其实是在B移入C,但是我们只要吧A-B反过来看待,也就是把B看作A,A看作B,这时 我们又回到了移动两个盘子的过程)

如果有N个盘子:

把除了最下面的N-1个盘子移动到B,N号盘子移动到C,N-1个盘子再移动到C即可;(最后一步最难理解)

}

代码如下:

#include <stdio.h>
int i =0;
void buzou(int num,int from,int to)
{
<span style="white-space:pre">	</span>i++;
<span style="white-space:pre">	</span>printf("第%d步:%d号盘子从%c移动到%c去\n",i,num,from+'a',to+'a');
}
void move(int x,int from,int temp,int to)//X是移动多少个
{
<span style="white-space:pre">	</span>if (x==1)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>buzou(x,from,to);
<span style="white-space:pre">	</span>} 
<span style="white-space:pre">	</span>else
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>move(x-1,from,to,temp);//先将要移动的盘子上面的所有盘子都移动到暂用盘
<span style="white-space:pre">		</span>buzou(x,from,to);//将要移动的盘移动到目标盘
<span style="white-space:pre">		</span>move(x-1,temp,from,to);//将暂用盘的移动到目标盘
<span style="white-space:pre">	</span>}
}
int main()
{
<span style="white-space:pre">	</span>int x;
<span style="white-space:pre">	</span>scanf("%d",&x);


<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>int from=0,temp=1,to=2;
<span style="white-space:pre">	</span>::i=0;
<span style="white-space:pre">	</span>move(x,from,temp,to);
<span style="white-space:pre">		</span>
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>return 0;
}
那么,谈到递归,就会想到一个更NB点的问题,

8皇后问题:经典的八皇后问题,即在一个8*8的棋盘上放8个皇后,使得这8个皇后无法互相攻击( 任意2个皇后不能处于同一行,同一列或是对角线上),输出所有可能的摆放情况。

这一题比汉诺塔来说难了许多(个人感觉);

因为核心思想是回溯算法,

我先给出回溯框架给大家(网上找的):

1: int a[n];
   2: try(int i)
   3: {
   4:     if(i>n)
   5:        输出结果;
   6:      else
   7:     {
   8:        for(j = 下界; j <= 上界; j=j+1)  // 枚举i所有可能的路径
   9:        {
  10:            if(fun(j))                 // 满足限界函数和约束条件
  11:              {
  12:                 a[i] = j;
  13:               ...                         // 其他操作
  14:                 try(i+1);
  15:               回溯前的清理工作(如a[i]置空值等);
  16:               }
  17:          }
  18:      }
  19: }
解释下大概思路就是:如果我有一个数组A[5],而且每一个A中元素都可以从1取到5;那么我先把A[0]赋值为1,如果可以的话,我就看下一行,也就是A[1]将他赋值为1,如果可以,继续下一行,如果不行,把他的值改为2,再看行不行,如果可以的话,继续,如果1-5都不行,不用管他,自动回到上一行,我再将A[0]赋值为2,继续往下走,如果A【0】到A【4】我都赋值了,那么输出,然后又自动返回,一直到所有都试过;

那么现在,8皇后问题的思路就很明了了:

将第一行(或者列)中第一个位置放一个皇后,然后判断是否满足条件,如果可以,看下一行第一个位置能不能放,如果能,继续下一行,如果不能,放第二个位置,一直到8行(或者列)全部放完,输出。

#include <stdio.h>
int q[9]={0};
int count=0;
bool panduan(int x,int y)//(x,y)对应行和列
{
	for(int i=0;i+1<y;i++)//按列遍历,I是列
	{
		if (x==q[i]||x+y==1+i+q[i]||x-y==q[i]-1-i)//判断不能再同一行一列或者一条斜线上
		{
			return false;
		} 
	}
	return true;
}
void put()
{
	printf("第%d种解法:\n",count);
	for (int i=0;i<=7;i++)//第1-8列
	{
		for(int j=1;j<=8;j++)//第1-8行
		{
			if (q[i]==j)
			{
				printf("1 ");
			} 
			else
			{
				printf("0 ");
			}
		}
		printf("\n");	
	}
	printf("\n");
}
void solve(int r)//R是列数-1
{
	if (r==8)
	{
		count++;
		put();
		
		return ;
	} 
	else
	{
		for (int i=1;i<=8;i++)//行
		{
			q[r]=i;//第R+1列皇后在第I行
			if (panduan(i,r+1))
			{
				solve(r+1);
			} 
			
		}
	}
}
int main()
{
	//q[0]=1;//第一列的第一个作为初始皇后位置
	solve(0);
	printf("\n解法一共%d种\n",count);
	return 0;
}

最难的地方在于那个solve函数,我的代码其实有很多不好的地方,比如如果用行来遍历,会好看很多,还有注意我的列,其实0列是第一列,但是行的话我的取值是从1-8的;

一开始我就建立了一个q[9],他的意思是,建立了9个列,当赋值的时候q[0]=3;意思就是第一列第三行放一个皇后,这样大家应该看得懂了。如果有错,可以直接私聊我。欢迎指正。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值