1. 程式人生 > 其它 >分治法(1)旋轉矩陣,拿金幣,棋盤覆蓋

分治法(1)旋轉矩陣,拿金幣,棋盤覆蓋

分治法:

參考:

(36條訊息) 分治演算法詳細講解(含經典例題分析)_nan_black的部落格-CSDN部落格_分治演算法幾個經典例子

(36條訊息) 經典演算法(1)分治法_胡亂huluan的部落格-CSDN部落格

(1);把a問題分成若干個小問題

(2):保證每一個小問題的解法都相同,並保證這些小問題能整合為a問題的解

(3):用遞迴或者迴圈來解每一個小問題

(4):把所有小問題的解整合為a問題的解

1.旋轉矩陣:

我們可以通過電腦構造一個螺旋方陣
例如,一個邊長為5的螺旋方陣樣子如下
1 16 15 14 13
2  17     24  23     12
3  18     25  22     11


4  19     20  21     10
5   6      7    8       9

思路:

把這個方陣分成多層,每層的階數為(s4個部分(藍,紅,綠,灰)

每個部分都是s-1個數,求解每一部分只需要改變行和列

然後用相同的方法求裡層(s-2

最後把所有的分解整合為總問題的解

#include<bits/stdc++.h>
using namespace std;
int a[1000][1000];
int size;
void fz()
{
    int s=size;
    int i=0,j=0,num=1;
    int b;
    for(;s!=0;s-=2)//結束標誌為方陣的階數為1或零
{ if(s==1)//階數為1時,直接把num放進方陣中並跳出 { a[i][j]=num; break; } for(b=0;b<s-1;b++,i++)//用一個變數b來控制數量 { a[i][j]=num; num++; } for(b=0;b<s-1;b++,j++) { a[i][j]=num; num
++; } for(b=0;b<s-1;b++,i--) { a[i][j]=num; num++; } for(b=0;b<s-1;b++,j--) { a[i][j]=num; num++; } i++; j++; } for(int i=0;i<=size-1;i++) { for(int j=0;j<=size-1;j++) { printf("%5d",a[i][j]); } cout<<endl; } } int main() { cin>>size; fz(); return 0; }

 

2.典型二分法:

問題:金塊問題

老闆有一袋金塊(共n塊),最優秀的僱員得到其中最重的一塊,最差的僱員得到其中最輕的一塊,假設有一臺比較重量的儀器,我們希望用最少的比較次數找出最重的金塊

方法:二分遞迴

思路

  1. 三種情況:數量為1,數量為2,數量大於2
  2. 第二種情況直接用max找兩數最大即可
  3. 第三種情況使用二分法mid=(c+d)/2 ,la=fmax(a,b,mid),ra=fmax(a,mid,d)
  4. 分別找資料前二分之一和後二分之一的值(通過遞迴求值,最後每一組的最小分化一定是兩數比較,即:d-c=1
  5. 從而變為第二種方法求解最大值)最後返回la和ra的最大者。

    Max(n)=Max(1+n/2)+Max(n/2,n)

    #include<bits/stdc++.h>
    using namespace std;
    int a[1000];
    int m;
    int fmax(int a[],int b,int c)
    {
        if(b==c)
        {
            return a[b];
        }
        if(c-b==1)
        {
            int la=a[b];
            int ra=a[c];
            return (max(la,ra));
        }
        if(c-b>=2)
        {
            int mid=(b+c)/2;
            int la=fmax(a,b,mid);
            int ra=fmax(a,mid,c);
            return (max(la,ra));
        }
    }
    int fmin(int a[],int b,int c)
    {
        if(b==c)
        {
            return a[b];
        }
        if(c-b==1)
        {
            int la=a[b];
            int ra=a[c];
            return (min(la,ra));
        }
        if(c-b>=2)
        {
            int mid=(b+c)/2;
            int la=fmin(a,b,mid);
            int ra=fmin(a,mid,c);
            return (min(la,ra));
        }
    }
    int main()
    {
        cout<<"請輸入金塊數量:";
        cin>>m;
        cout<<"請輸入每塊金塊的重量:"; 
        for(int i=0;i<m;i++)
        {
            cin>>a[i];
        }
        int maxn=fmax(a,0,m-1);
        int minn=fmin(a,0,m-1);
        cout<<"最重的金塊:"<<maxn<<endl;
        cout<<"最輕的金塊:"<<minn<<endl;
    }

     

    3.二分法不相似情況

    需要構造與原問題相似子問題。

    問題:殘缺棋盤

    題目:(37條訊息) 分治演算法-殘缺棋盤_gzj_1101的部落格-CSDN部落格_殘缺棋盤

    思路:

    1. 把棋盤二分為更小的棋盤,每次都切成四部分
    2. 由於原棋盤中只有一個特殊方格,這樣劃分後,這四個較小的棋盤中只有一個子棋盤包含特殊方格,其餘三個棋盤中沒有特殊方格。為了將這三個沒有特殊方格的子棋盤轉化為特殊棋盤,以便採用遞迴方法求解,可以用一個L型骨牌覆蓋這三個較小棋盤的匯合處,從而轉化為四個較小規模的棋盤覆蓋問題。遞迴地使用這種劃分策略,直到將棋盤分割為1*1的子棋盤

    ——《演算法設計與分析——以acm大學程式競賽線上題庫為例》(清華大學出版社(北京))p68-69

    //(tr,tc)為棋盤中右上角的方格座標
    //(dr,dc)為特殊方格的座標
    //size為行/列數
    #include<bits/stdc++.h>
    using namespace std;
    int a[1024][1024];
    int tr,dr,tc,dc,size=1,k,title=1; 
    void fz(int tr,int tc,int dr,int dc,int size)
    {
        if(size==1)//結束標誌:棋盤已經被劃分為1*1
        return;
        int s=size/2; //再次劃分棋盤
        int t=title++;//L型骨牌序號
        //左上角 
        if(dr<tr+s&&dc<tc+s)
        {
            fz(tr,tc,dr,dc,s);
        }
    //若不在左上角則用t號L骨牌覆蓋右下角(與其他子棋盤的交匯處)
        else    
    {
            a[tr+s-1][tc+s-1]=t;
     //覆蓋其餘方格
            fz(tr,tc,tr+s-1,tc+s-1,s);
        }
        //右上角
        if(dr<tr+s&&dc>=tc+s)
        {
            fz(tr,tc+s,dr,dc,s);
        }
        else
        {
            a[tr+s-1][tc+s]=t;
            fz(tr,tc+s,tr+s-1,tc+s,s);
        }
        //左下角 
        if(dr>=tr+s&&dc<tc+s)
        {
            fz(tr+s,tc,dr,dc,s);
        }
        else
        {
            a[tr+s][tc+s-1]=t;
            fz(tr+s,tc,tr+s,tc+s-1,s);
        }
        //右下角 
        if(dr>=tr+s&&dc>=tc+s)
        {
            fz(tr+s,tc+s,dr,dc,s);
        }
        else
        {
            a[tr+s][tc+s]=t;
            fz(tr+s,tc+s,tr+s,tc+s,s);
        }
     }
    int main()
    {   cin>>k;
        cin>>dr>>dc;
        for(int i=1;i<=k;i++)
        {
            size=size*2;//根據棋盤的階數求出棋盤的行和列
        }
        fz(0,0,dr,dc,size);
        for(int i=0;i<size;i++)
        {
            for(int j=0;j<size;j++)
            {
                printf("%-3d",a[i][j]);
            }
            printf("\n");
        }
        return 0;
    }

    //其他三個小棋盤的求法和左上方大體相同,只需要改變tr和tc,不做過多註釋。