Blog·Tanky WooABOUTRSS

汉诺塔,n皇后,跳马问题汇总---Tanky Woo

30 Sep 2010
这篇博客是从旧博客 WordPress 迁移过来,内容可能存在转换异常。

软件课讲了这些问题,这次顺便总结下。 

 说白了也就是:递归,回溯,深搜或者广搜。 

 1.汉诺塔 

 //////////////////////////////////////////////// /* 汉诺塔 题目: 假设有A, B, C 3个轴,有n个直径各不相同, 从小到大依次编号为1,2,3,...,n的圆盘 按照从小到大的顺序叠放在A轴上。现在要求 将这n个圆盘移至C轴上并仍然按照同样顺序 叠放,但圆盘移动时必须遵守下列规则: 1.每次只能移动一个圆盘,它必须位于某个   轴的顶部。 2.圆盘可以插在A,B,C中任一轴上。 3.任何时刻都不能将一个较大的圆盘压在较小   的圆盘之上。 */ /////////////////////////////////////////////// 

经典的问题,属于递归的入门级问题,但是同样不好分析,在n<=4以内还可以模拟下汉诺塔的实现,当n>=5时就不太现实了,让我们来看看汉诺塔当圆盘个数n时有多少组解? 按照传说来看:n=64,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。 

 但是这毕竟是神话,不过当把64个金片全部放到另外一根针时,确实要很长很长一段时间。  让我们来看看需要多长时间。   首先,我们找出递推关系:   f(n + 1) = 2*f(n) + 1   至于这个怎么得到的可以画图看看。   把递推关系算出来后,也就是:   f(n) = 2^n-1   那么当n=64时,是多少?   f(64)= 2^64-1=18446744073709551615    假如每秒钟一次,共需多长时间呢?一年大约有 31536926 秒,计算表明移完这些金片需要5800多亿年,比地球寿命还要长,事实上,世界、梵塔、庙宇和众生都已经灰飞烟灭。 

好吧,说了那么多,还是步入正题。

汉诺塔的实现有递归和非递归两种情况,递归的很常见,也很简单,非递归实际上就是二叉树的中序遍历。也可以认为是栈的实现。

递归的版本:

/*递归实现*/
#include 
using namespace std;

//把n号圆盘从x移到y,并打印出。
void Move(int n, char x, char y)
{
  cout<< "把" << n << "号圆盘从" << x << "移动到" << y << endl;
}

//把前n个通过b从a移到c
void Hanoi(int n, char a, char b, char c)  
{
    if(n == 1)
        Move(1, a, c);
    else
    {
        Hanoi(n-1, a, c, b);
        Move(n, a, c);
        Hanoi(n-1, b, a, c);
    }
}

int main()
{
    int n;
    cout << "输入n的大小: ";
    cin >> n;
    Hanoi(n, 'a', 'b', 'c');
    cout << "Ok!" << endl << "By Tanky Woo." << endl;
    return 0;
}

非递归的版本有时间再补上:

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

这是非递归实现的二叉树的中序遍历:

 http://www.cppleyuan.com/viewthread.php?tid=650 


2.n皇后

对于每一个ACMer,八皇后问题都是必经之路。

 作为搜索类题目还是老问题: 

 1.边界条件。   2.对每种情况都得遍历到,可以用解答树分析。   3.剪枝 http://www.wutianqi.com/?p=1341(搜索与剪枝)  4.辅助空间的变化。回溯前和回溯后的变化。   如果不用辅助空间的回溯当然就不需要注意辅助空间的问题了。 

以下是n皇后的源码: 

/*
*  n皇后问题
*  Tanky Woo
*/

#include 
using namespace std;

int queen[100];
int n;         // n皇后
int tot = 0;   //解法种数

// www.wutianqi.com
void search(int cur)
{
    if(cur == n)   //递归边界。符合要求,输出。
    {
        tot++;
        for(int i=0; i> n;
    search(0);
    cout << "共有" << tot << "种解." << endl << "By Tanky Woo." << endl;
    return 0;
}

对于这个问题,还可以用辅助空间来提高算法的效率: 增加辅助空间vis[][]来判断是否有其他皇后已经在列和对角线上。 

#include 
using namespace std;

int queen[100];
int n;         // n皇后
int tot = 0;   //解法种数


int vis[3][100];   // 辅助空间
void search(int cur)
{
    if(cur == n)   //递归边界。符合要求,输出。
    {
        tot++;
        for(int i=0; i> n;
    search(0);
    cout << "共有" << tot << "种解." << endl << "By Tanky Woo." << endl;
    return 0;
}

3.跳马问题:  据说此题证明可以用组合数学中的哈密顿环。 组合数学确实博大精深,看过一段时间的组合数学,感觉和实际联系的很多,Orz. 此题有两种版本: 

 ①:给定一个N*N的棋盘,起始点在(0,0)处,要求求出有多少种方法,可以不重复的遍历棋盘上所有的点。     规则:1.马走日字            2.走过的点就不能再走了 

 此题和上面的n皇后类似,是标准的DFS。  分析:从起始点开始,每次遍历八种方向,直到边界条件,并输出。 

以下是跳马问题一的源码: 

/*马跳棋盘问题*/

#include 
using namespace std;
const int N = 10;
int a[N][N] = {0};
int cnt = 0;

void Horse(int a, int b, int t);
// www.wutianqi.com
int main()
{
    int i = 0, j = 0, t = 1;
    a[i][j] = t;
    Horse(i, j, step+1);
    cout << cnt << endl;
    cout << "By Tanky Woo.\n";
    return 0;
}
void Horse(int a, int b, int t)
{
    int x[4] ={-2, -1, 1, 2}, y[4] = {-2, -1, 1, 2};  
    if(t == N*N+1)  
        cnt++;

    for(int i=0; i<4; ++i)
        for(int j=0; j<4; ++j)
        {
            if(x[i]==y[j] || x[i]==-y[j])  
                continue;
            if(a+x[i]>=0 && a+x[i]=0 && b+y[j]![](http://bgy.gd.cn/diannao/2000/rhy/aosaihs-1-2.files/aosaih2.gif)  

此题也是OI上很有名的骑士问题。
此题似乎比较适合BFS. 
还没尝试过。 
  

* * *

 
让我再想想,好像还有八数码和素数环问题没写。
不过在HDOJ上遇到过一个素数环的题目:
[http://www.wutianqi.com/?p=1329](http://www.wutianqi.com/?p=1329)
有兴趣可以做下。

对于DFSBFS更多的题目,可以在我博客右上角搜索栏里输入DFSBFS,会出来相应题目。