从汉诺塔到八皇后问题

来源:互联网 发布:像素骑士团合战数据 编辑:程序博客网 时间:2024/06/17 05:52

汉诺塔题目:

在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的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;意思就是第一列第三行放一个皇后,这样大家应该看得懂了。如果有错,可以直接私聊我。欢迎指正。

 

0 0
原创粉丝点击