分治---二分搜索,棋盘覆盖

来源:互联网 发布:java输出菱形 编辑:程序博客网 时间:2024/06/16 15:26

参考http://blog.csdn.net/liufeng_king/article/details/8477868

1、分治法

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

     分治法所能解决的问题一般具有以下几个特征:
  1) 该问题的规模缩小到一定的程度就可以容易地解决
  2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
  3) 利用该问题分解出的子问题的解可以合并为该问题的解;
  4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

     分治法的基本步骤
     分治法在每一层递归上都有三个步骤:
  分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
  解决若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
  合并将各个子问题的解合并为原问题的解。
  它的一般的算法设计模式如下:

Divide-and-Conquer(P)  1. if |P|≤n0  2. then return(ADHOC(P))  3. 将P分解为较小的子问题 P1 ,P2 ,...,Pk  4. for i←1 to k  5. do yi ← Divide-and-Conquer(Pi) △ 递归解决Pi  6. T ← MERGE(y1,y2,...,yk) △ 合并子问题  7. return(T)

其中|P|表示问题P的规模;n0为一阈值,表示当问题P的规模不超过n0时,问题已容易直接解出,不必再继续分解。ADHOC(P)是该分治法中的基本子算法,用于直接解小规模的问题P。因此,当P的规模不超过n0时直接用算法ADHOC(P)求解。算法MERGE(y1,y2,...,yk)是该分治法中的合并子算法,用于将P的子问题P1 ,P2 ,...,Pk的相应的解y1,y2,...,yk合并为P的解。

子问题的划分人们从大量实践中发现,在用分治法设计算法时,最好使子问题的规模大致相同。换句话说,将一个问题分成大小相等的k个子问题的处理方法是行之有效的。许多问题可以取 k = 2。这种使子问题规模大致相等的做法是出自一种平衡(balancing)子问题的思想,它几乎总是比子问题规模不等的做法要好。

2、二分搜索

大部分程序员应该都知道二分搜索的大致原理,这里不再赘述。需要说明的是二分搜索是所有以比较为基础的搜索算法时间复杂度最低的算法。用二叉树描速二分查找算法,最坏情况下与二叉树的最高阶相同。比较二叉树线性查找也可用二叉树表示,最坏情况下比较次数为数组元素数量。

任何一种以比较为基础的搜索算法,其最坏情况所用时间不可能低于O(logn)

二分搜索代码实现:

也可见http://blog.csdn.net/qq_22238021/article/details/78225069

#include <iostream>#include <stdio.h>using namespace std;template<class Type>int BinarySearch(Type a[],const Type& x,int n);int main(){    int x = 6;    int a[10];    for(int i=0; i<10; i++)        a[i] = i + 1;    cout<<BinarySearch(a,x,10)<<endl;    return 0;}template<class Type>int BinarySearch(Type a[],const Type& x,int n){//a[0...n-1]中搜索x    int left = 0;    int right = n-1;    while(left<=right)    {        int mid = (left + right)/2;        if(x == a[mid])            return mid;        if(x>a[mid])            left = mid + 1;        else            right = mid - 1;    }    return -1;//未找到x}

3、棋盘覆盖

棋盘覆盖问题。有一个2k2k的方格棋盘,恰有一个方格是黑色的,其他为白色。你的任务是用包含3个方格的L型牌覆盖所有白色方格。黑色方格不能被覆盖,且任意一个白色方格不能同时被两个或更多牌覆盖。如图所示为L型牌的4种旋转方式。 

        分治三步骤:
划分问题:将2k2k的棋盘划分为2k12k1这样的子棋盘4块。
递归求解:递归填充各个格子,填充分为四个情况,在下面会有解释,递归出口为k=0也就是子棋盘方格数为1。
合并问题:不需要合并子问题。
递归填充的四种情况:
如果黑方块在左上子棋盘,则递归填充左上子棋盘;否则填充左上子棋盘的右下角,将右下角看做黑色方块,然后递归填充左上子棋盘。
如果黑方块在右上子棋盘,则递归填充右上子棋盘;否则填充右上子棋盘的左下角,将左下角看做黑色方块,然后递归填充右上子棋盘。
如果黑方块在左下子棋盘,则递归填充左下子棋盘;否则填充左下子棋盘的右上角,将右上角看做黑色方块,然后递归填充左下子棋盘。
如果黑方块在右下子棋盘,则递归填充右下子棋盘;否则填充右下子棋盘的右下角,将左上角看做黑色方块,然后递归填充右下子棋盘。 实现:

每次都对分割后的四个小方块进行判断,判断特殊方格是否在里面。这里的判断的方法是每次先记录下整个大方块的左上角(top left coner)方格的行列坐标,然后再与特殊方格坐标进行比较,就可以知道特殊方格是否在该块中。如果特殊方块在里面,这直接递归下去求即可,如果不在,这根据分割的四个方块的不同位置,把右下角、左下角、右上角或者左上角的方格标记为特殊方块,然后继续递归。在递归函数里,还要有一个变量s来记录边的方格数,每次对方块进行划分时,边的方格数都会减半,这个变量是为了方便判断特殊方格的位置。其次还要有一个变tile来记录L型骨牌的数量。

代码:

#include <iostream>#include <stdio.h>using namespace std;int tile = 1;//全局变量 骨牌编号int Board[4][4];//棋盘void ChessBoard(int tr,int tc,int dr,int dc,int size);int main(){    for(int i=0; i<4; i++)        for(int j=0; j<4; j++)            Board[i][j] = 0;    ChessBoard(0,0,2,3,4);    for(int i=0; i<4; i++)    {        for(int j=0; j<4; j++)            cout<<Board[i][j]<<" ";        cout<<endl;    }}/** * tr : 棋盘左上角的行号      tc棋盘左上角的列号 * dr : 特殊方格左上角的行号  dc特殊方格左上角的列号 * size :size = 2^k 棋盘规格为2^k*2^k */void ChessBoard(int tr,int tc,int dr,int dc,int size){    if(size == 1) return;    int t = tile++;//L型骨牌编号    int s = size/2;//分割棋盘    //覆盖左上角子棋盘    if(dr<tr+s && dc<tc+s)//特殊方格在此棋盘中        ChessBoard(tr,tc,dr,dc,s);    else//特殊方格不在此棋盘中    {        //用编号为t的骨牌覆盖右下角        Board[tr+s-1][tc+s-1] = t;        //覆盖其余方格        ChessBoard(tr,tc,tr+s-1,tc+s-1,s);    }    //覆盖右上角子棋盘    if(dr<tr+s && dc>=tc+s)//特殊方格在此棋盘中        ChessBoard(tr,tc+s,dr,dc,s);    else//特殊方格不在此棋盘中    {        //用编号为t的骨牌覆盖左下角        Board[tr+s-1][tc+s] = t;        //覆盖其余方格        ChessBoard(tr,tc+s,tr+s-1,tc+s,s);    }    //覆盖左下角子棋盘    if(dr>=tr+s && dc<tc+s)//特殊方格在此棋盘中        ChessBoard(tr+s,tc,dr,dc,s);    else//特殊方格不在此棋盘中    {        //用编号为t的骨牌覆盖右上角        Board[tr+s][tc+s-1] = t;        //覆盖其余方格        ChessBoard(tr+s,tc,tr+s,tc+s-1,s);    }    //覆盖右下角子棋盘    if(dr>=tr+s && dc>=tc+s)//特殊方格在此棋盘中        ChessBoard(tr+s,tc+s,dr,dc,s);    else//特殊方格不在此棋盘中    {        //用编号为t的骨牌覆盖左上角        Board[tr+s][tc+s] = t;        //覆盖其余方格        ChessBoard(tr+s,tc+s,tr+s,tc+s,s);    }}


覆盖一个2^k*2^k大小的棋盘需要(4^k-1)/3个L型骨牌


原创粉丝点击