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

 

转载于:https://www.cnblogs.com/cao-lei/p/6891648.html

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用 C 语言编写的分治法循环赛日程表的代码: ```c #include <stdio.h> #include <stdlib.h> void roundRobin(int n, int **table, int start) { if (n == 1) { table[0][start] = 1; return; } int **newTable = (int **)malloc(n * sizeof(int *)); for (int i = 0; i < n; i++) { newTable[i] = (int *)malloc(n * sizeof(int)); } int m = n / 2; roundRobin(m, newTable, start); for (int i = 0; i < m; i++) { for (int j = 0; j < m; j++) { table[i][j + m] = newTable[i][j] + m; table[i + m][j] = newTable[i][j] + m; table[i + m][j + m] = newTable[i][j]; } } for (int i = 0; i < n; i++) { for (int j = m; j < n - 1; j++) { int k = (i + j - m + start) % (n - 1); table[i][k] = table[i][j]; table[i][j] = n - 1 + table[i][j]; } } for (int i = 0; i < n; i++) { free(newTable[i]); } free(newTable); } int main() { int n; printf("请输入参赛队伍的数量:"); scanf("%d", &n); int **table = (int **)malloc(n * sizeof(int *)); for (int i = 0; i < n; i++) { table[i] = (int *)malloc(n * sizeof(int)); for (int j = 0; j < n; j++) { table[i][j] = -1; } } roundRobin(n, table, 0); printf("循环赛日程表如下:\n"); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (table[i][j] < n - 1) { printf("%d vs %d\t", i + 1, table[i][j] + 1); } } printf("\n"); } for (int i = 0; i < n; i++) { free(table[i]); } free(table); return 0; } ``` 在这个代码中,我们使用分治法来构造循环赛日程表。我们先将所有队伍分成两组,然后递归地构造这两组的循环赛日程表,然后再将它们合并起来得到整个循环赛日程表。具体的合并方法是,对于左上、右上和左下三个子矩阵,将它们的元素加上一个固定的偏移量,然后再将右上和左下矩阵交换,得到一个新的矩阵,然后将这个矩阵的右半部分

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值