递归与分治策略

一、概念级:

    直接或间接地调用自身的算法称为递归算法。用函数自身给出函数的定义的函数称为递归函数。

     例:

              阶乘的递归定义


                                                     

        分治的基本原理思想是将一个规模为n的问题分解成为k个规模较小的问题,这些子问题的相互独立且与原问题相同。递归的解这些子问题,然后将各个子问题的解合并到原问题的解。

divide-and-conquer{
     if(|P|<=n0) adhoc(P);
     divide P into smaller sub-instances P1,P2,P3....Pk
     for(int i=0 ; i<=k ; i++){
         yi=divide-and-conquer(Pi);
     }   
     return merge(y1,y2,...yk);
}

二、问题集

     1.hanoi 问题
     设a,b,c为三个塔座,开始时,在塔座a上一共叠有n个大小依次增大的圆盘,各圆盘的编号为1,2,3......n。现要求将塔座a上的所有圆盘移动到塔座b上,并仍按同样的顺序叠置,问移动步骤。
     规则:每次只可以移动一个圆盘,任何时候都不允许大圆盘压在小圆盘之上。
     代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int step=1;
void hanoi(int n,char a,char b,char c){
    if(n>0){
        hanoi(n-1,a,c,b);
        cout<<step++<<": "<<a<<" -> "<<b<<endl;
        hanoi(n-1,c,b,a);
    }
}
int main(){
    int n;
    while( printf("please input the number: ")&&cin>>n){
        step=1;
        hanoi(n,'A','B','C');
    }
    return 0;
}


2.二分搜索
     给定一组元素,现找到所有元素中的一个特定元素。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int a[100];
template <class type>
int bin_search(type *a,type x,int n){
    int l=0,r=n-1;
    while(l<=r){
        int mid=(l+r)>>1;
        if(a[mid]==x) return mid;
        else if(a[mid]>x) r=mid-1;
        else l=mid+1;
    }
    return -1;//no this element
}
int main(){
    int n;
    while(cin>>n){
        for(int i=0;i<n;i++)
            cin>>a[i];
        int ans=bin_search(a,3,n);
        if(ans+1)
            cout<<"the element at "<<ans+1<<endl;
        else cout<<"no this element!"<<endl;
    }
    return 0;
}


3.大整数的乘法
将n位二进制整数X和Y都分为2部分,每段的长为n/2位(假设n是2 的幂)
假设                                               X=A2 n/2 +B,     Y=C2 n/2 +D;
即有:
                                                      XY=(A2 n/2 +B)*(C2 n/2 +D)
                                                            =AC2 n +(AD+BC)2 n/2 +BD
     由上式可知,进行一个乘法计算需要4次n/2位的整数乘法(AC,AD,BC,BD),以及2次不超过2n位的整数加法,此外还需要2次移位。假设加法和移位的时间复杂度为O(n)。设T(n)是2 个位数为n的整数相乘所需的运算总数,则有:
                                                            
由此可知,T(n)=O(n 2 ) ,可以看出这和普通的计算方法并无改进。为此我们对计算方法进行了改变:
                        XY=AC2 n +((A-B)*(D-C)+AC+BD)2 n/2 +BD
此式看上去更加复杂,但是我们分析一下他的计算过程,可知他由3次的整数乘法和6次的整数加减法,以及2次位移,可得:
                                                              
容易求得其解为T(n)=O(n log3 )=O(n 1.59 ),可见本次优化是巨大的。
4,棋盘覆盖
       在一个2 k *2 k 个方格组成的棋盘中,若恰有一个方格与其他方格不同,则称该方格为一个特殊方格,且该棋盘为一个特殊棋盘。  在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
                                                                                         
                                                           

       当k>0时,将 2 k *2 k 棋盘分割为4个2 k-1 ×2 k-1 子棋盘(a)所示。特殊方格必位于4个较小子棋盘之一中,其余3个子棋盘中无特殊方格。为了将这3个无特殊方格的子棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这3个较小棋盘的会合处,如 (b)所示,从而将原问题转化为4个较小规模的棋盘覆盖问题。递归地使用这种分割,直至棋盘简化为棋盘1×1。
       实现:每次都对分割后的四个小方块进行判断,判断特殊方格是否在里面。这里的判断的方法是每次先记录下整个大方块的左上角(top left coner)方格的行列坐标,然后再与特殊方格坐标进行比较,就可以知道特殊方格是否在该块中。如果特殊方块在里面,这直接递归下去求即可,如果不在,这根据分割的四个方块的不同位置,把右下角、左下角、右上角或者左上角的方格标记为特殊方块,然后继续递归。在递归函数里,还要有一个变量s来记录边的方格数,每次对方块进行划分时,边的方格数都会减半,这个变量是为了方便判断特殊方格的位置。其次还要有一个变nCount来记录L型骨牌的数量。
                                                            

代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
int matrix[100][100];
int ncount=1;
void chessboard(int tr,int tc,int dr,int dc,int size)   //tr:棋盘左上角的行号列号,dr:特殊方格的行号列号
{
    if(size==1) return ;
    size/=2;
    int t=ncount++;
    if(tr+size>dr&&tc+size>dc) chessboard(tr,tc,dr,dc,size);
    else{
        matrix[tr+size-1][tc+size-1]=t;
        chessboard(tr,tc,tr+size-1,tc+size-1,size);
    }
    if(tr+size>dr&&tc+size<=dc) chessboard(tr,tc+size,dr,dc,size);
    else{
        matrix[tr+size-1][tc+size]=t;
        chessboard(tr,tc+size,tr+size-1,tc+size,size);
    }
    if(tr+size<=dr&&tc+size>dc) chessboard(tr+size,tc,dr,dc,size);
    else{
        matrix[tr+size][tc+size-1]=t;
        chessboard(tr+size,tc,tr+size,tc+size-1,size);
    }
    if(tr+size<=dr&&tc+size<=dc) chessboard(tr+size,tc+size,dr,dc,size);
    else{
        matrix[tr+size][tc+size]=t;
        chessboard(tr+size,tc+size,tr+size,tc+size,size);
    }
}
int main(){
    int size,r,c,row,col;
    scanf("%d",&size);
    scanf("%d%d",&row,&col);
    chessboard(0,0,row,col,size);
    for(int i=0;i<size;i++){
        for(int j=0;j<size;j++)
            cout<<matrix[i][j]<<" ";
        cout<<endl;
    }
    return 0;
}

                                     
5.快速排序
快速排序步骤:
•找出一个元素作为基准,然后对数组进行分区操作
•使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值
•如此作为基准的元素调整到排序后的正确位置。
•递归快速排序,将其他n-1个元素也调整到排序后的正确位置。
•最后每个元素都是在排序后的正确位置,排序完成。
我在这拿6 1 2 7 9 3 4 5 10 8这个数组描述,算法过程如下图,依次递归不断更新区间并排序。

        代码:
#include <iostream>
using namespace std;
template <class type>
int Partition(type *a,int p,int r){
    int i=p,j=r+1;
    type x=a[p];
    while(1){
        while(a[--j]>x&&j>p);
        while(a[++i]<x&&i<r);
        if(i>=j) break;
        swap(a[i],a[j]);
    }
    a[p]=a[j];
    a[j]=x;
    return j;
}
template <class type>
void Quick_sort(type *a,int p,int r){
    if(p<r){
        int q=Partition(a,p,r);
        Quick_sort(a,p,q-1);
        Quick_sort(a,q+1,r);
    }
}
int main(){
    int a[100],n,l,r;
    while(cin>>n){
        for(int i=0;i<n;i++) cin>>a[i];
        Quick_sort(a,0,n-1);
        for(int i=0;i<n;i++) cout<<a[i]<<' ';
        cout<<endl;
    }
    return 0;
}



  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值