算法思想-分治

分治(Divide-and-Conquer)

分治法,字面意思是“分而治之”,就是把一个复杂的问题分成两个或多个相同的子问题,再把子问题分成更小的子问题直到最后子问题可以简单地直接求解,原问题的解即子问题的解的合并,这里父子问题不存在依赖关系,即父问题在解决时可以把子问题当做1个成员,子问题在求解是也可以完全脱离父问题的状态。
但并不是所有复杂问题都可以完美拆分成多个子问题,要想使用分治思想必须满足如下条件:

  • 该问题的规模缩小到一定的程度就可以容易的解决。
  • 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
  • 利用该问题分解出的子问题的解可以合并为该问题的解。
  • 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。

第3、4条最为关键,假如不满足这两个条件,即表示子问题解会影响到上一级问题的解,此时不能使用分治来简单地组合各个子问题解获得最终解,而是使用动态规划。
分治法的总体步骤为三步:

  • 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;

  • 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;

  • 合并:将各个子问题的解合并为原问题的解。

分治子问题的独立性与无后效性决定,分治天然与递归手段是一对。即分治大多都是采用递归来进行的,例如经典的汉诺塔游戏。下面将以汉诺塔递归公式的推导来解释分治的前提条件与解决步骤。

汉诺塔游戏:

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

其中的有用信息:

  • 三根宝石针,要求将一根针上的所有金片移动到另一根针上
  • 每次只能移动一片金币
  • 任何时刻,大金片都要在小金片上面

计算过程

很明显,金币数量决定了游戏的复杂度,针其实是没有顺序的,我们用红黄绿来表示

1. 一块金币

  一块金币只需移动一步,就可将红色针上的金币移动到黄色针上:
  
  1--------------------------------->1
  红 ++++黄++++ 绿___________ 红 ++++黄++++ 绿  

2. 两块金币

首先将1号金币放到绿色针,2号金币位置不变:

 ----------------------------------------->1
 ____________________________2
 红 ++++黄++++ 绿___________ 红 ++++黄++++ 绿  

 再将2号金币放到黄色针:

 —————————————————————————————————————————1
 ---------------------------------->2
 红 ++++黄++++ 绿___________ 红 ++++黄++++ 绿  

 最后将1号金币放到黄色针,此时已将所有金币从一个针移动到另一个针上面(红到黄):
 
 ----------------------------------1<---------
 __________________________________2
 红 ++++黄++++ 绿___________ 红 ++++黄++++ 绿  


 针对两块金币的情况,可归纳出一个典型的步骤:
 1、首先将1号金币放到一根针(本例选绿针)
 2、再将2号金币放到另一根针(只剩下黄针)
 3、将1号金币放到2号金币上面(黄针)

3. 三块金币
1
2
3
红 ++++黄++++ 绿___________ 红 ++++黄++++ 绿

 3块金币情况,我们先假设1、2号金币为一个整体称之为0号金币,且小于3号金币,此时问题简化为如下:
 
 0
 3
 红 ++++黄++++ 绿___________ 红 ++++黄++++ 绿  

是不是非常熟悉,这其实与两块金币情况是一样的,我们可以继续使用上面的三步将0、3号金币放到黄色针上。

 1、首先将0号金币放到绿针
 2、再将3号金币放到黄针
 3、将0号金币放到黄针
 
 但是这仅仅是个假设,0号金币不存在,而实际上是两块金币。那么上述的第一步“首先将0号金币放到一根针”实际是指将1、2
 金币放到绿针,仔细回忆,这是不是又变成了”把两个金币从一根针移动到另一根针“的问题?没错,此时根本不用考虑3号金币,
 因为在这个阶段,它一直老老实实的待在红针上。只是这次的目标针为绿色,那么我们就执行以下三步:
 1.1、首先将1号金币放到黄针
 1.2、再将2号金币放到绿针
 1.3、将1号金币放到绿针
 此时情况应该是这样的:
 
 ----------------------------------------->0(1、2)
 3
 红 ++++黄++++ 绿___________ 红 ++++黄++++ 绿  
 
 执行完“首先将0号金币放到绿针”,继续执行“再将3号金币放到黄针”,这步很简单
 
 ___________________________________________0(1、2)
 ---------------------------------->3
 红 ++++黄++++ 绿___________ 红 ++++黄+++++++绿  

再来看“将0号金币放到黄针”,很明显这又变成了一个“把两个金币从一根针移动到另一根针“的问题,那么就做下面三步:
 2.1、首先将1号金币放到红针
 2.2、再将2号金币放到黄针
 2.3、将1号金币放到黄针
 此时情况应该是这样的:
 
 ------------------------------------>0(1、2)
 _____________________________________3
 红 ++++黄++++ 绿--------------红 ++++黄++++ 绿  

至此,需总结如下:

  1. 当金币数等于2时,将所有金币从一个位置移动到另一个位置所需的步骤是固定的三步
  • 首先将1号金币放到一根针
  • 再将2号金币放到另一根针
  • 最后将1号金币放回到2号金币之上
    很明显,这是一个子问题,而且这个子问题满足独立性(不受后面操作影响)满足分治法要求3。
  1. 金币数很多时,要依次递归,直到金币数量变为1时,此时无需再次拆解,因为1个金币直接移动即可,这一过程就是分治法步骤中提到的“分解”

  2. 分解完成后,利用”三步骤“完成子任务,这就是分治法中的“解决”

  3. 各层子任务完成后,会跳到上一层递归,继续完成父任务,直到到递归栈的最外层,此过程则是结果的“合并”

下图是描述了4枚金币时的递归过程、子任务解决以及结果合并的全过程
在这里插入图片描述
从最左侧开始,首先需解决四枚金币的情况。假设前三枚金币为一个整体,那么四枚金币这一层可分为三步,其中的第一步变为三枚金币的情况,继续分解。直到n=1,即金币数目为1,此时停止分解(递归)。

分解完成,此时并没有移动任何一枚金币,从最深一层递归栈开始,反序依次执行"三步骤",执行完毕后跳出本层递归栈,继续执行上一层的“三步骤”,最终完成4枚金币的移动。图中绿色虚线即为执行顺序流。

代码实现

引用一个C语言实现实例

#include <stdio.h>
#include <string.h>
/*
 算法思路:1将 n-1个盘子先放到B座位上
          2.将A座上地剩下的一个盘移动到C盘上
          3、将n-1个盘从B座移动到C座上
*/
//函数声明
void move(int n, char x, char y);
void hannuo(int n, char one, char two, char three)
{
  	if(n==1)
  		move(n, one, three); //递归截止条件
  	else
	{
  		hannuo(n-1, one, three,two);//递归分解任务:将 n-1个盘子先放到B座位上
 		move(n, one, three);//将A座上地剩下的一个盘移动到C盘上
  		hannuo(n-1, two, one, three);//递归分解任务:将n-1个盘从B座移动到C座上
	}
}
void move(int n, char x,char y)
{
    static step = 1;
 	printf("step:%d, move golden[%d] to %c  from  %c",y,x)
 	if(step++ %3 ==0)
 		printf("\n");
}
 
int main()
{
 	int n;
 	printf("input your number");
 	scanf("%d",&n);
 	hannuo(n,'A','B','C');
 	return 0;
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值