zoukankan      html  css  js  c++  java
  • 算法入门3:分治算法(下)

    分治算法的设计模式 - 大化小,小化了

    分治算法的主要步骤就是:分解,求解,合并。

    [cpp] view plain copy
    1. Divide-and-Conquer(P)  
    2. {  
    3.          //问题规模足够小,直接解决  
    4.     if(P≤n0) return(ADHOC(P);  
    5.    
    6.          //问题规模大,则分解为较小的子问题 P1 ,P2 ,...,Pk  
    7.     divide p into smaller subinstance P1 ,P2 ,...,Pk  
    8.    
    9.          //递归解决每个小问题  
    10.     for i = 1 to k  
    11.                  yi =  Divide-and-Conquer(Pi)  
    12.    
    13.          //合并子问题的解  
    14.     T =  MERGE(y1,y2,...,yk)            
    15.    
    16.     return(T)  
    17. }  

    经典问题

    (1)二分搜索

    (2)大整数乘法

    (3)Strassen矩阵乘法

    (4)棋盘覆盖

    (5)合并排序

    (6)快速排序

    (7)线性时间选择

    (8)最接近点对问题

    (9)循环赛日程表

    二分搜索

    问题:从一个已经排好序的序列中,查找某一个元素。

    分析:假设序列数组为a,大小为n,从小到大的顺序排列。按照最基本的思路,那就是遍历一遍数组,时间复杂度为O(n)。如果n很大,效率显然不够高,因为没有把题目中“已经排好序” 的条件用上。既然是已经排好序,那么就可以进行折半查找(二分搜索)。方法如下:

    1. 将数组从中间分成上下两半,如果中间的值刚好是要查找的元素,直接输出结果。

    2. 如果中间的值比要查找的大,那么说明要查找的元素只可能出现在上半段,再对上半段进行二分搜索。

    3. 如果中间的值比要查找的小,那么说明要查找的元素只可能出现在下半段,再对下半段进行二分搜索。

    这就是分治算法最典型的例子。每次把规模为n的问题划分成n/2,继续划分成n/4,n/8… 直到足够小能直接解决。

    代码:

    [cpp] view plain copy
    1. /************************************************************************  
    2.  * 名  称:BinarySearch.cpp  
    3.  * 功  能:分治算法案例:二分查找  
    4.  * 作  者:JarvisChu  
    5.  * 时  间:2013-11-7  
    6.  ************************************************************************/   
    7.    
    8. #include "stdio.h"  
    9.    
    10. /*----------------------------------------------------------------------------------  
    11.  * 功  能:   在大小为n的数组a中查找元素tag 
    12.  * 参  数:   a[] 要查找的数组,从小到大排列 
    13.                           n 数组大小  
    14.                           tag 待查找的元素 
    15.  * 返  回: 找到则返回所在下标,否则返回-1  
    16.  ------------------------------------------------------------------------------------*/    
    17. int BinarySearch(int a[],int n,int tag)    
    18. {  
    19.          int low = 0,high = n-1,mid = 0;  
    20.    
    21.          while(low<=high)  
    22.          {  
    23.                  mid = (low+high)/2;  
    24.    
    25.                  //正好是中间的元素  
    26.                  if(a[mid] == tag)  
    27.                           return mid;  
    28.    
    29.                  //在上半段查找  
    30.                  if(a[mid]>tag)  
    31.                  {  
    32.                           high = mid-1;  
    33.                  }  
    34.    
    35.                  //在下半段查找  
    36.                  else  
    37.                  {  
    38.                           low = mid+1;  
    39.                  }  
    40.          }  
    41.    
    42.          return -1;  
    43. }  
    44.    
    45. int main()  
    46. {  
    47.          //Test  
    48.          int a[10]={0,1,2,3,4,5,6,7,8,9};  
    49.          printf("%d ",BinarySearch(a,10,4));  
    50.          printf("%d ",BinarySearch(a,10,11));  
    51.    
    52.          return 0;  
    53. }  

    棋盘覆盖

    问题:  在一个2k×2k 个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。

          

    分析:   当k>0时,将2k×2k棋盘分割为4个2k-1×2k-1子棋盘(a)所示。

    特殊方格必位于4个较小子棋盘之一中,其余3个子棋盘中无特殊方格。为了将这3个无特殊方格的子棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这3个较小棋盘的会合处,如 (b)所示,可以把黄色的方格看做是较小棋盘的特殊方格,从而将原问题转化为4个较小规模的棋盘覆盖问题。

    递归地使用这种分割,直至棋盘简化为棋盘1×1。

    代码:

     

    [cpp] view plain copy
    1. /************************************************************************  
    2.  * 名  称:Chessboard.cpp 
    3.  * 功  能:分治算法案例:棋盘覆盖  
    4.  * 作  者:JarvisChu  
    5.  * 时  间:2013-11-7  
    6.  ************************************************************************/   
    7.    
    8. #include <stdio.h>  
    9.    
    10. /* L型骨牌的类型 
    11.   
    12. ------           ------|            |                         | 
    13. |                      |            |                         | 
    14. |                      |            |------             ------| 
    15.   
    16. */  
    17. #define L1 1  
    18. #define L2 2  
    19. #define L3 3  
    20. #define L4 4  
    21.    
    22. #define N 16            //棋盘大小,边长  
    23.    
    24. int board[N][N]; //棋盘  
    25.    
    26. /*----------------------------------------------------------------------------------  
    27.  * 功  能: 对左上角为(tr,tc),边长为size的棋盘进行棋盘覆盖       
    28.  * 参  数:(tr,tc)为棋盘的左上角坐标;(dr,dc)为特殊方格的位置;size为棋盘的大小,即边长 
    29.  * 返  回:无  
    30.  ------------------------------------------------------------------------------------*/    
    31. void ChessBoard(int tr,int tc,int dr,int dc,int size)  
    32. {  
    33.          //棋盘为1 x 1,不可再分割,直接返回  
    34.          if(size == 1) return ;  
    35.    
    36.          //分割子棋盘  
    37.          int s = size/2;//子棋盘大小  
    38.    
    39.          //判断特殊方格在哪个子棋盘内,以确定使用哪种L骨牌  
    40.          int type = 0;  
    41.          if(dr < tr+s && dc < tc+s)//特殊方格在左上角的子棋盘内  
    42.          {  
    43.                  type = L4; //使用第四种类型的L骨牌  
    44.          }  
    45.          else if(dr < tr + s && dc >= tc + s) //在右上角  
    46.          {  
    47.                  type = L3;  
    48.          }  
    49.          else if(dr >= tr + s && dc < tc + s) //在左下角  
    50.          {  
    51.                  type = L2;  
    52.          }  
    53.          else                                 //在右下角  
    54.          {  
    55.                  type = L1;  
    56.          }  
    57.    
    58.           
    59.          //覆盖左上角的子棋盘  
    60.          if(type == L4)           //特殊方格在该子棋盘内  
    61.          {  
    62.                  ChessBoard(tr,tc,dr,dc,s);//递归覆盖该子棋盘  
    63.          }  
    64.          else                    //特殊方格不在该子棋盘内  
    65.          {  
    66.                  board[tr+s-1][tc+s-1] = type;//使用type型骨牌覆盖其右下角的方格  
    67.                  ChessBoard(tr,tc,tr+s-1,tc+s-1,s);  
    68.          }  
    69.    
    70.          //覆盖右上角的子棋盘  
    71.          if(type == L3)        //特殊方格在该子棋盘内  
    72.          {  
    73.                  ChessBoard(tr,tc+s,dr,dc,s);        
    74.          }  
    75.          else                 //特殊方格不在该子棋盘内  
    76.          {  
    77.                  board[tr+s-1][tc+s] = type;//使用type型骨牌覆盖其左下角的方格  
    78.                  ChessBoard(tr,tc+s,tr+s-1,tc+s,s);  
    79.          }  
    80.    
    81.          //覆盖左下角的子棋盘  
    82.          if(type == L2)      //特殊方格在该子棋盘内  
    83.          {  
    84.                  ChessBoard(tr+s,tc,dr,dc,s);        
    85.          }  
    86.          else                //特殊方格不在该子棋盘内  
    87.          {  
    88.                  board[tr+s][tc+s-1] = type; //使用type型骨牌覆盖其右上角的方格  
    89.                  ChessBoard(tr+s,tc,tr+s,tc+s-1,s);  
    90.          }  
    91.    
    92.          //覆盖右下角的子棋盘  
    93.          if(type == L1)         //特殊方格在该子棋盘内  
    94.          {  
    95.                  ChessBoard(tr+s,tc+s,dr,dc,s);      
    96.          }  
    97.          else                   //特殊方格不在该子棋盘内  
    98.          {  
    99.                  board[tr+s][tc+s] = type;//使用type型骨牌覆盖其左上角的方格  
    100.                   ChessBoard(tr+s,tc+s,tr+s,tc+s,s);  
    101.          }  
    102. }  
    103.    
    104.    
    105. int main()  
    106. {  
    107.          //初始化棋盘  
    108.          for(int row=0;row<N;++row)  
    109.          {  
    110.                  for(int col=0;col<N;++col)  
    111.                           board[row][col] = 0;  
    112.          }  
    113.    
    114.          //设定特殊方格位置  
    115.          int dr = 3,dc=4;  
    116.           
    117.          //棋盘覆盖  
    118.          ChessBoard(0,0,dr,dc,N);  
    119.    
    120.    
    121.          //打印棋盘  
    122.          for(int row=0;row<N;++row)  
    123.          {  
    124.                  for(int col=0;col<N;++col)  
    125.                           printf("%3d",board[row][col]);  
    126.                  printf(" ");  
    127.          }  
    128.    
    129.          return 0;  
    130. }  


    备注:博主用MFC实现了一个图形化的棋盘覆盖程序。如有兴趣可以留下邮箱索要源码。

    可以设置棋盘的格数,特殊方格(红色)的位置,点击开始演示,则执行棋盘覆盖

     

    转载本文请注明作者和出处

    作者 :JarvisChu

    出处:http://blog.csdn.NET/jarvischu

  • 相关阅读:
    c# 泛型(知识整理)
    [VC++]C\C++中结构体知识点强化
    [VC++]CString转化成char
    [VC++]C++中类的多态与虚函数的使用
    [C#]关于自己编写MesasgeBox
    [C#]给DataGridView里的ComboBoxCol添加SelectIndexChange事件
    [C#]用代码触发一个事件
    [C#]序列化例子
    [VC++]怎么使对话框中的按钮DISABLE和ENABLE
    [VC++]控制台程序窗口隐藏
  • 原文地址:https://www.cnblogs.com/aabbcc/p/6504520.html
Copyright © 2011-2022 走看看