分治问题

Table of Contents

1 分治算法

分治(divide-and-conquer)算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互 独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。。这个技巧是很多高效算法的基础,如排序算 法(快速排序,归并排序),傅立叶变换(快速傅立叶变换),二叉树搜索等。

2 分治法求最大值

为了更好的理解分治算法,在进入正题前我们先去一个简单的例子。下面以求数组里N个项中最大的项为例进行说明。

  • 我们可以通过普通的数组遍历求出
for(t=a[0],i=1;i<N;i++)
    if(a[i]>t)
        t=a[i];
  • 通过分治法
Item max(Item a[],int l,int r)
{
    Item u,v;
    int m=(l+r)/2;
    if(l==r)
        return a[l];
    u=max(a,l,m);
    v=max(a,m,r);
    if(u>v)
        return u;
    else
        return v;
}

3 汉诺塔(tower of Hanoi)问题

对于递归,对于分治问题,如果不讨论古老的汉诺塔问题,好像有点不完整。

3.1 汉诺塔问题的递归解

它指定每一步应该移动那个圆盘,以及移动的方向("+"表示向右移动装上的盘,但到达最右端的时候循环到最左 端;"-" 表示向左移动装上的盘,但到达最左端的时候循环到最右端。)

3.1.1 思路

要想从某根移动N个盘到后面的柱子上:

  1. 首先移动N-1个小盘到最左边的柱子上(内部递归性)
  2. 其次把第N个圆盘移动到右边的柱子上
  3. 接着,(递归性的)将N-1个圆盘放到第N个盘的上面就完成了。

3.1.2 解决方案

void hanoi(int N,int d)
{
    if(N==0)
        return;
    hanoi(N-1,-d); //对应1
    shift(N,d);    //对应2
    hanoi(N-1,d);  //对应3
}

3.1.3 具体实现

#include <stdio.h>
static  int count;
void shift(int N,int d)
{
    if(d>0)
        printf("+");
    printf("%d\n",N*d);
    ++count;
}
void hanoi(int N,int d)
{
    if(N==0) return ;
    hanoi(N-1,-d);
    shift(N,d);
    hanoi(N-1,-d);
}
int main(int argc, char *argv[])
{
    int N;
    printf("请输入要移动的汉诺塔层数:\n");
    
    scanf("%d",&N);
    hanoi(N,1); // 1表示右移,-1表示左移
    printf("总的步数为:%d\n",count);
    return 0;
}

 

3.1.4 性质

汉诺塔问题的递归分治算法得到的解需要2N -1步移动。

3.2 分治法绘制标尺

这个问题和汉诺塔有什么关系呢?让我们来看看:

3.2.1 问题描述

在一个尺子上,每隔1英寸就在1/2英寸处画上一个标记,以1/4英寸为间隔画上稍短的标志,一次类推。我们设计 一个程序以任意分辨率画出这些标志。
我们重新设置比例让任务变成在0和2n 之间每个点出画一个标记。以n=3为例,如图:

3.2.2 递归实现

void rule(int l,int r,int n)
{
    int m=(r+l)/2;
    if(h>0)
    {
        rule(l,m,h-1);
        mark(m,h);
        rule(m,r,h-1);
    }
}

3.2.3 分析

当函数中n=3时,长度为8的标尺得到的标尺长度分别为:1、2、1、3、1、2、1。通过结果可以看出,长度序列与 汉诺塔问题中移动圆盘的序列完全一样。实际上递归程序是相同的缘故。这两个程序都是基本的分治方法的演化。

3.3 二进制与汉诺塔的关系

注意看图中,偶数行中尾部0的个数。是不是也一样,是不是很神奇,哈哈。

3.4 结论

对于汉诺塔问题,n位数字所蕴含的就是该问题的一个简单的算法。我们如果在实际中怎么来移动上面的圆盘呢:

  1. 如果我们想把整个圆盘移动到右边的柱子上,则
    1. 若圆盘的个数N为奇数,将最小盘想同方向移动。
    2. 若圆盘的个数N为偶数,将最小盘想反方向移动。
  2. 不包括小盘,进行唯一一步合法的操作。(就是我们在移动了最小的盘之后,另外两个柱子上唯一合法的操作 就是把较小的盘移动的较大的盘上)。
  3. 每隔一步移动一下最小盘,原则同1。(就如同标尺上没隔一个刻度都是最短的一样)。

Date: 2012-07-20 14:41:37

Author: Crowning

Org version 7.8.11 with Emacs version 23

Validate XHTML 1.0
posted @ 2012-07-20 14:39  csqlwy  阅读(718)  评论(0编辑  收藏  举报