算法与数据结构-分治法及汉诺塔问题求解

来源:互联网 发布:软件开放平台 编辑:程序博客网 时间:2024/06/06 19:48

序言

学习五大常用算法:回溯 + 贪心 + DP + 分治 + 分支界限

这里看分治法的概念及应用,并用汉诺塔问题举例


1. 分治法的概念及应用领域

  • 分治法

    • 分而治之:将问题分解为相互独立的与原问题相同或相似的子问题,直到最后子问题可以直接求解,原问题的解即为子问题解的合并。

      在之前复习常见排序算法的时候我们就已经用过这种算法了,快排、归并排序中,都有类似的子问题划分求解合并的过程。

  • 分治策略

    • 一般来说,问题求解难度随着问题规模的增加而增加。对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
    • 要点:最优子结构性质 + 子问题相互独立
  • 能用分治法求解的问题四个特征

    • 问题规模缩小至一定规模就可解决(大多数问题都可以满足)
    • 问题可分解为多个规模较小的相同子问题(最优子结构性质,分治法的前提)
    • 子问题的解可以合并为原问题的解(区别于贪心和动态规划)
    • 该问题分解的子问题是相互独立的(分治法的效率,避免重复求解)
  • 三个步骤

    • 分解(Divide):将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
    • 解决(Conquer):若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
    • 合并(Combine):将各个子问题的解合并为原问题的解
  • 分治法求解的经典问题

    • 二分查找
    • 快排
    • 归并排序
    • 汉诺塔
    • 大整数乘法
    • Strassen矩阵乘法
    • 棋盘覆盖
    • 线性时间选择
    • 循环赛日程表


2. 汉诺塔问题求解

  • 问题描述

    在汉诺塔游戏中,有三个分别命名为A、B、C得塔座,几个大小各不相同,从小到大一次编号得圆盘,每个原盘中间有一个小孔。最初,所有得圆盘都在A塔座上,其中最大得圆盘在最下面,然后是第二大,以此类推

  • 游戏规则

    • 一次只能移动一个圆盘
    • 任何时候都不能将一个较大的圆盘压在较小的圆盘上面
    • 除了第二条限制,任何塔座的最上面的圆盘都可以移动到其他塔座上
  • 问题分析

现有三根柱子,我们分别称为 1柱,2柱,3柱.欲将1柱上的n个盘子移到3柱上:    >> 把1柱n-1个盘子移到过2柱上(借助3柱为过渡柱)    >> 把1柱上第n个盘子移到3柱    >> 将2柱的n-1个盘子移至3柱(借助1柱为过渡柱)到第三步我们发现,已经开始重复之前的过程,每次的目的都是将源柱盘子中最底端最大的盘子移动到目标柱上。呈现出递归的特点。总结一般规律:    (1) 3个盘子的时候移动过程:1->3, 1->2,3->2,1->3,2->1,2->3,1->3. 一共7步完成移动。    (2) 如果是4个盘子,将上方3个盘子由1柱移动到2柱,需要7步,将第4个盘子移动到3柱上,需要1步,最后又将2柱上的3个盘子移动到3柱上,由(1)可知,也需要7步。一共需要:7+1+7=15步    (3) 如果是5个盘子,将上方4个盘子由1柱移动到2柱,需要15步,将第5个盘子移动到3柱上,需要1步,最后又将2柱上的4个盘子移动到3柱上,也需要15步。一共需要:15+1+15=31步    (4) 如果是6个盘子,...,一共需要:31+1+31=63步    一般规律是移动n个盘子,需要 2^n - 1 步。
  • 代码实现-<递归,C语言>
#include <stdio.h>int i;void HanNuoTa(int n, char src, char trans, char dest){    if (n == 1)         //n==1,直接将盘子从源柱移动到目标柱即可        printf("第%d步:将第%d个盘子从%c--->%c\n",i++, n, src, dest);    else    {        HanNuoTa(n - 1, src, trans, dest);                             //先将n-1盘子移动到过渡柱        printf("第%d步:将第%d个盘子从%c--->%c\n",i++, n, src, dest);    //将第n个盘子移动到目标柱        HanNuoTa(n - 1, trans, src, dest);                             //重复以上步骤,以源柱为过渡柱    }}int main(){    int n;    while (scanf("%d", &n))    //scanf()成功返回读取的数据项数,文件尾返回NULL    {        i = 1;        HanNuoTa(n, '1','2','3');        printf("总的步数%d\n", --i);    }    //或HanNuoTa(n, "1柱", "2柱", "3柱");函数原型为HanNuoTa(int n, char *src, char *trans, char *dest);    return 0;}



Acknowledgements:
http://www.cnblogs.com/xsyfl/p/6921687.html
http://blog.csdn.net/geekwangminli/article/details/7981570 (推荐)
http://www.cnblogs.com/AI-Algorithms/p/3357368.html

2017.09.21

阅读全文
0 0