分治法(1)旋轉矩陣,拿金幣,棋盤覆蓋
阿新 • • 發佈:2022-04-02
分治法:
參考:
(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
思路:
把這個方陣分成多層,每層的階數為(s)4個部分(藍,紅,綠,灰)
每個部分都是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,數量為2,數量大於2
- 第二種情況直接用max找兩數最大即可
- 第三種情況使用二分法mid=(c+d)/2 ,la=fmax(a,b,mid),ra=fmax(a,mid,d)
- 分別找資料前二分之一和後二分之一的值(通過遞迴求值,最後每一組的最小分化一定是兩數比較,即:d-c=1
- 從而變為第二種方法求解最大值)最後返回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部落格_殘缺棋盤
思路:
- 把棋盤二分為更小的棋盤,每次都切成四部分
- 由於原棋盤中只有一個特殊方格,這樣劃分後,這四個較小的棋盤中只有一個子棋盤包含特殊方格,其餘三個棋盤中沒有特殊方格。為了將這三個沒有特殊方格的子棋盤轉化為特殊棋盤,以便採用遞迴方法求解,可以用一個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,不做過多註釋。