[C++] 分治法之棋盤覆蓋、循環賽日程表


一、分治的基本思想

  將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。

  對於一個規模為 n 的問題,若問題可以容易地解決,則直接解決,否則將其分解為 k 個規模較小的子問題,這些子問題互相獨立且與原問題形式相同,遞歸地解這些子問題,然后將各子問題的解合並得到原問題的解。

二、用分治法求解問題的主要步驟

  1、分解:將原問題分解為若干規模較小、相互獨立、與原問題形式相同的子問題;

  2、解決:若子問題規模較小而容易被解決則直接解決,否則,遞歸地解各個子問題;

  3、合並:將各子問題的解合並得到原問題的解。

三、分治法實例

  1、棋盤覆蓋

  在一個 2k * 2個方格組成的棋盤中,有一個方格與其它的不同,若使用以下四種 L 型骨牌覆蓋除這個特殊方格的其它方格,如何覆蓋。四個 L 型骨牌如下圖:

圖1.1 L型骨牌

       棋盤中的特殊方格如圖:

圖1.2 存在特殊方格的棋盤
  覆蓋完成后的棋盤:
圖1.3 覆蓋完成的棋盤
 1 #include<iostream>
 2 using namespace std;
 3 
 4 int tile = 0;
 5 int Board[4][4];    //棋盤 
 6 
 7 /*
 8 tr:棋盤左上角方格的行號 
 9 tc:棋盤左上角方格的列號
10 dr:特殊方格所在的行號 
11 dc:特殊方格所在的列號
12 size:棋盤的規格(size * size)
13 */
14 void ChessBoard(int tr,int tc , int dr, int  dc, int size)
15 {
16     if(size == 1) return;
17     int t =tile++,        //L型骨牌號 
18         s = size/2;        //分割棋盤 
19     //覆蓋左上角子棋盤 
20     if(dr < tr+s && dc < tc+s)
21         //特殊方格在此棋盤中 
22         ChessBoard(tr,tc,dr,dc,s);
23     else
24     {    //此棋盤中無特殊方格
25         //用t號L型骨牌覆蓋右下角 
26         Board[tr+s-1][tc+s-1] = t;
27         //覆蓋其余方格 
28         ChessBoard(tr,tc,dr,dc,s);
29     }
30     
31     //覆蓋右上角子棋盤
32     if(dr < tr+s && dc >= tc+s)
33         //特殊方格在此棋盤中
34         ChessBoard(tr,tc+s,dr,dc,s);
35     else
36     {    //此棋盤中無特殊方格
37         //用t號L型骨牌覆蓋左下角
38         Board[tr+s-1][tc+s] = t;
39         //覆蓋其余方格 
40         ChessBoard(tr,tc+s,tr+s-1,tc+s,s);
41     }
42     
43     //覆蓋左下角子棋盤
44     if(dr >= tr+s && dc < tc+s)
45         //特殊方格在此棋盤中
46         ChessBoard(tr+s,tc,dr,dc,s);
47     else
48     {    //此棋盤中無特殊方格
49         //用t號L型骨牌覆蓋右上角
50         Board[tr+s][tc+s-1] = t;
51         //覆蓋其余方格 
52         ChessBoard(tr+s,tc,tr+s,tc+s-1,s);
53     }
54     
55     //覆蓋右下角子棋盤
56     if(dr >= tr+s && dc >= tc+s)
57         //特殊方格在此棋盤中
58         ChessBoard(tr+s,tc+s,dr,dc,s);
59     else
60     {    //此棋盤中無特殊方格
61         //用t號L型骨牌覆蓋左上角
62         Board[tr+s][tc+s] = t;
63         //覆蓋其余方格 
64         ChessBoard(tr+s,tc+s,tr+s,tc+s,s);
65     }
66 
67 }
68 
69 int main()
70 {
71     ChessBoard(0 , 0 , 1 , 3 , 4);
72     //輸出覆蓋完成后的棋盤
73     for(int i = 0 ; i < 4; i++)
74     {
75         for(int j = 0 ; j < 4; j++)
76         {
77             cout<<Board[i][j];
78         }
79         cout<<endl;
80     }
81     return 0;
82 }

  2、循環賽日程表

  設有 n = 2個運動員要進行網球循環賽。現要設計一個滿足以下要求的比賽日程表:

    (1)每個選手必須與其他n-1個選手各賽一次;

    (2)每個選手一天只能參賽一次;

    (3)循環賽在n-1天內結束

     請按此要求將比賽日程表設計成有 n 行和 n-1 列的一個表。在表中的第 i 行,第 j 列處填入第 i 個選手在第 j 天所遇到的選手。其中 1 ≤ i ≤ n,1 ≤ j ≤ n-1。8 個選手的比賽日程表如下圖:

 1 #include<iostream>
 2 using namespace std;
 3 
 4 int a[100][100];
 5 int n;    //選手的個數
 6 
 7 /*
 8 tox:目標數組的行號 
 9 toy:目標數組的列號 
10 fromx:源數組的行號 
11 fromy:源數組的列號 
12 r:數組的大小為 r*r 
13 */
14 void Copy(int tox, int toy, int fromx, int fromy, int r)
15 {
16     for(int i = 0; i < r; i++)
17         for(int j = 0; j < r; j++)  
18             a[tox+i][toy+j] = a[fromx+i][fromy+j];
19 }
20 
21 void Table(int k)
22 {    
23     n = 1 << k;    
24     //構造正方形表格的第一行數據
25     for(int i = 0; i < n; i++)
26         a[0][i] = i + 1;
27     //采用分治算法,構造整個循環賽日程表
28     for(int r = 1; r < n; r <<= 1)
29         for(int i = 0; i < n; i += 2*r)
30         { 
31             Copy(r, r + i, 0, i, r);        //左上角復制到右下角 
32             Copy(r, i, 0, r + i, r);        //右上角復制到左下角 
33         }
34 }
35 
36 
37 int main()
38 {
39     int k;
40     cout<<"請輸入k的值:";
41     cin>>k; 
42     
43     Table(k);
44     
45     for(int i = 0; i < n; i++)
46     {
47         for(int j = 0; j < n; j++)
48         {
49             cout<< a[i][j] << " ";
50         }
51         cout<<endl;
52     } 
53     return 0;
54 }

 


免責聲明!

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



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