分治法之棋盤覆蓋問題


寫此博文目的:

1.剛學了棋盤覆蓋問題,自己實現它,加深自己的理解很感悟

2.給為棋盤問題困惑的朋友帶來一點思路

 

開始分析!

 

什么叫做分治法呢?

:簡單來說就是分而治之,先把問題分解成很多個小問題,然后再處理它

棋盤覆蓋問題就是一個很經典的分治問題

首先我們先來看一下棋盤覆蓋問題到底是個什么問題?

題目引用自:https://blog.csdn.net/acm_jl/article/details/50938164

思路分析:

將一個大的棋盤划分為相同大小的四塊,在這四塊相同大小的子棋盤中,現在只有一個子棋盤有一個格子是不可覆蓋的,還有三個子棋盤是所有的格子都是可以覆蓋的,所以我們需要為這3個不存在不可覆蓋格子的子棋盤構造3個不可覆蓋的格子,那么我們應該如何構造呢?

下面我們看一張圖:

假如現在第一個不可覆蓋的格子在左上角,那么我們需要構造的3個不可覆蓋的格子就跟上面的圖一樣,構造的這3個不可覆蓋的格子的連接成的形狀肯定是個L型,只是會隨着棋盤中不可覆蓋的格子的位置的不同而L型的開口方向會有所變化

總結一下:

2k2k2k∗2k的棋盤划分為2k12k12k−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,目的就是把一個大棋盤划分為四個相同大小的子棋盤。

運行結果:

 

不足錯誤的地方,歡迎各位拍磚指正哦!!!!!

技術在於分享


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM