从0开始学算法--基础算法(3.5分治)

  分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。在计算机科学中,分治法就是运用分治思想的一种很重要的算法。分治法是很多高效算法的基础,如排序算法(快速排序归并排序),傅立叶变换(快速傅立叶变换)等等。

例一:最大连续子序列和

解:如果把序列分成相等的两部分,已知每一部分以左端点开始的最大连续和,以右端点结束的最大连续和,以及它的最大连续子序列的和

 那么如图所示,整个序列的最大连续子序列和要么是左半部分的最大连续和,要么是右半部分的最大连续和,要么是左边以右端点结束的最大连续和加右边以左端点开始的最大连续和

分成两部分之后,对于每一部分再次等分处理,知道每一块的大小为1,这种不断分解的方法就是分治。

因为此题有时间复杂度更低的做法,这种做法不给出代码。

例二:棋盘分割问题。k

在一个2k*2k的白色棋盘上有一个点被涂成黑色,请用L形的方块占满所有格子(方块不能重叠),输出方案。为什么4k次方一定是3的倍数

 对于一个8*8的网格,我们将它分为4个4*4的网格,此时标记点只在一个4*4的网格中,没有标记点的3个4*4的网格相接处必然有一个L形的位置,我们把这个地方标记上,此时问题就变成了4个4*4的网格,每个网格都有一点涂黑,对于每个4*4的网格分成4个2*2的网格递归处理即可

代码:

const int N = 1e6+5;
int a[100][100];
int cnt=1;
 
void f(int tr,int tc,int dr,int dc,int flag){
    if(flag == 1)
        return ;
    int t = cnt++; //L型骨牌号
    int s = flag/2;  //分割棋盘
    //覆盖左上
    if(dr <tr+s&& dc<tc+s)//特殊方格在此棋盘
        f(tr,tc,dr,dc,s);
    else{//无特殊方格
        //用t号L方格来覆盖右下角
        a[tr+s-1][tc+s-1]=t;
        //覆盖其余方格
        f(tr,tc,tr+s-1,tc+s-1,s);
    }
    //覆盖右上角
    if(dr <tr+s&& dc>=tc+s)
        f(tr,tc+s,dr,dc,s);
    else{
        //用t号L方格来覆盖左下角
        a[tr+s-1][tc+s]=t;
        f(tr,tc+s,tr+s-1,tc+s,s);
    }
    //左下角
    if(dr >=tr+s&& dc<tc+s)
        f(tr+s,tc,dr,dc,s);
    else{
        //用t号L方格来覆盖右上角
        a[tr+s][tc+s-1]=t;
        f(tr+s,tc,tr+s,tc+s-1,s);
    }
    //右下角
    if(dr >=tr+s&& dc>=tc+s)
        f(tr+s,tc+s,dr,dc,s);
    else{    //用t号L方格来覆盖左上角
        a[tr+s][tc+s]=t;
        f(tr+s,tc+s,tr+s,tc+s,s);
    }
 
 
}
 
 
int main(){
    memset(a,0,sizeof(a));
    int k,dr,dc;
    cin>>k>>dr>>dc;
    int tmp=pow(2,k);
    f(0,0,dr,dc,tmp);
    for(int i = 0 ;i<tmp;i++){
        for(int j = 0;j<tmp;j++){
            cout<<a[i][j]<<" ";
        }
        cout<<endl;
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wz-archer/p/11718568.html