寫此博文目的:
1.剛學了棋盤覆蓋問題,自己實現它,加深自己的理解很感悟
2.給為棋盤問題困惑的朋友帶來一點思路
開始分析!
什么叫做分治法呢?
:簡單來說就是分而治之,先把問題分解成很多個小問題,然后再處理它
棋盤覆蓋問題就是一個很經典的分治問題
首先我們先來看一下棋盤覆蓋問題到底是個什么問題?
題目引用自:https://blog.csdn.net/acm_jl/article/details/50938164
思路分析:
將一個大的棋盤划分為相同大小的四塊,在這四塊相同大小的子棋盤中,現在只有一個子棋盤有一個格子是不可覆蓋的,還有三個子棋盤是所有的格子都是可以覆蓋的,所以我們需要為這3個不存在不可覆蓋格子的子棋盤構造3個不可覆蓋的格子,那么我們應該如何構造呢?
下面我們看一張圖:
假如現在第一個不可覆蓋的格子在左上角,那么我們需要構造的3個不可覆蓋的格子就跟上面的圖一樣,構造的這3個不可覆蓋的格子的連接成的形狀肯定是個L型,只是會隨着棋盤中不可覆蓋的格子的位置的不同而L型的開口方向會有所變化
總結一下:
將2k∗2k2k∗2k的棋盤划分為2k−1∗2k−12k−1∗2k−1這樣的子棋盤4塊。遞歸填充各個格子,填充分為四個情況,歸出口為s=0,s=0也就是子棋盤方格數為1。
遞歸的四種情況:
如果黑方塊在左上子棋盤,則遞歸填充左上子棋盤;否則填充左上子棋盤的右下角,將右下角看做黑色方塊,然后遞歸填充左上子棋盤。
如果黑方塊在右上子棋盤,則遞歸填充右上子棋盤;否則填充右上子棋盤的左下角,將左下角看做黑色方塊,然后遞歸填充右上子棋盤。
如果黑方塊在左下子棋盤,則遞歸填充左下子棋盤;否則填充左下子棋盤的右上角,將右上角看做黑色方塊,然后遞歸填充左下子棋盤。
如果黑方塊在右下子棋盤,則遞歸填充右下子棋盤;否則填充右下子棋盤的右下角,將左上角看做黑色方塊,然后遞歸填充右下子棋盤。
好啦,話不多說,我們直接擼代碼吧!!!
1 #include<stdio.h> 2 #define max 1024 3 int cb[max][max];//最大棋盤 4 int id=0;//覆蓋標志位 5 int chessboard(int tr,int tc,int dr,int dc,int size)//tr,tc代表棋盤左上角的位置,dr ,dc代表棋盤不可覆蓋點的位置,size是棋盤大小 6 { 7 if(size==1)//如果遞歸到某個時候,棋盤大小為1,則結束遞歸 8 { 9 return 0; 10 } 11 int s=size/2;//使得新得到的棋盤為原來棋盤大小的四分之一 12 int t=id++; 13 if(dr<tr+s&&dc<tc+s)//如果不可覆蓋點在左上角,就對這個棋盤左上角的四分之一重新進行棋盤覆蓋 14 { 15 chessboard(tr,tc,dr,dc,s); 16 }else//因為不可覆蓋點不在左上角,所以我們要在左上角構造一個不可覆蓋點 17 { 18 cb[tr+s-1][tc+s-1]=t;//構造完畢 19 chessboard(tr,tc,tr+s-1,tc+s-1,s);//在我們構造完不可覆蓋點之后,棋盤的左上角的四分之一又有了不可覆蓋點,所以就對左上角棋盤的四分之一進行棋盤覆蓋 20 } 21 22 if(dr<tr+s&&dc>=tc+s)//如果不可覆蓋點在右上角,就對這個棋盤右上角的四分之一重新進行棋盤覆蓋 23 { 24 chessboard(tr,tc+s,dr,dc,s); 25 }else//因為不可覆蓋點不在右上角,所以我們要在右上角構造一個不可覆蓋點 26 { 27 cb[tr+s-1][tc+s]=t; 28 chessboard(tr,tc+s,tr+s-1,tc+s,s);//在我們構造完不可覆蓋點之后,棋盤的右上角的四分之一又有了不可覆蓋點,所以就對右上角棋盤的四分之一進行棋盤覆蓋 29 } 30 31 32 if(dr>=tr+s&&dc<tc+s)//如果不可覆蓋點在左下角,就對這個棋盤左下角的四分之一重新進行棋盤覆蓋 33 { 34 chessboard(tr+s,tc,dr,dc,s); 35 }else//因為不可覆蓋點不在左下角,所以我們要在左下角構造一個不可覆蓋點 36 { 37 cb[tr+s][tc+s-1]=t; 38 chessboard(tr+s,tc,tr+s,tc+s-1,s);//在我們構造完不可覆蓋點之后,棋盤的左下角的四分之一又有了不可覆蓋點,所以就對左下角棋盤的四分之一進行棋盤覆蓋 39 } 40 41 if(dr>=tr+s&&dc>=tc+s)//如果不可覆蓋點在右下角,就對這個棋盤右下角的四分之一重新進行棋盤覆蓋 42 { 43 chessboard(tr+s,tc+s,dr,dc,s); 44 }else//因為不可覆蓋點不在右下角,所以我們要在右下角構造一個不可覆蓋點 45 { 46 cb[tr+s][tc+s]=t; 47 chessboard(tr+s,tc+s,tr+s,tc+s,s);//在我們構造完不可覆蓋點之后,棋盤的右下角的四分之一又有了不可覆蓋點,所以就對右下角棋盤的四分之一進行棋盤覆蓋 48 } 49 50 //后面的四個步驟都跟第一個類似 51 } 52 int main() 53 { 54 printf("請輸入正方形棋盤的大小(行數):\n"); 55 int n; 56 scanf("%d",&n); 57 printf("請輸入在%d*%d棋盤上不可覆蓋點的位置:\n",n,n); 58 int i,j,k,l; 59 scanf("%d %d",&i,&j); 60 printf("不可覆蓋點位置輸入完畢,不可覆蓋點的值為-1\n"); 61 cb[i][j]=-1; 62 chessboard(0,0,i,j,n); 63 for(k=0;k<n;k++) 64 { 65 printf("%2d",cb[k][0]); 66 for(l=1;l<n;l++) 67 { 68 printf(" %2d",cb[k][l]); 69 } 70 printf("\n"); 71 } 72 return 0; 73 }
代碼分析:
我們先看chessboard函數
函數的主干是四個if else循環,也就是說只會執行一個if中的語句,但是會執行3個else中的語句,這3個else中的語句就是構造不可覆蓋格子,然后對含有新構造的不可覆蓋點的子棋盤來重寫進行棋盤覆蓋,也就是遞歸調用棋盤覆蓋函數,遞歸的結束條件就是子棋盤只有一個格子,也就是s=1,每次調用棋盤覆蓋函數,都要進行s=size/2,目的就是把一個大棋盤划分為四個相同大小的子棋盤。
運行結果:
不足錯誤的地方,歡迎各位拍磚指正哦!!!!!
技術在於分享