[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 }

 

posted @ 2017-05-22 21:53  C3Stones  阅读(4152)  评论(0编辑  收藏  举报