1.分治法的基本思想
将一个规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同,递归的解决这些子问题,然后将各子问题的解合并得到原问题的解。
2.算法设计的一般模式:
divide-and-conquer(p)
{
if(|p|<=n0) adhoc(p);//当问题的规模不超过n0时,问题已容易解出,不再分解,adhoc(P)用于直接解规模最小的问题
divide p into smaller subinstances P1,P2,...,Pk;
for(i=1;i<k;i++)
yi=divide-and-conquer(Pi);
return merge(y1,..,yk);//合并子算法
}
3.分治法的具体应用-循环赛日程表
3.1问题描述:
设有n=2^k个运动员要进行网球循环赛。要满足以下要求:
(1)每个选手必须与其他n-1个选手各赛一次
(2)每个选手一天只能赛一次
(3)循环赛一共进行n-1天
输入
一个小于等于7的正整数k
输出
循环赛日程表(1到2的k次方个人)
输入样例
3
输出样例
1 2 3 4 5 6 7 8
2 1 4 3 6 5 8 7
3 4 1 2 7 8 5 6
4 3 2 1 8 7 6 5
5 6 7 8 1 2 3 4
6 5 8 7 2 1 4 3
7 8 5 6 3 4 1 2
8 7 6 5 4 3 2 1
2 1 4 3 6 5 8 7
3 4 1 2 7 8 5 6
4 3 2 1 8 7 6 5
5 6 7 8 1 2 3 4
6 5 8 7 2 1 4 3
7 8 5 6 3 4 1 2
8 7 6 5 4 3 2 1
3.2问题分析
按分治策略,可以将所有选手分为两半,n个选手的比赛日程表就可以通过为n/2个选手设计的比赛日程表来决定。递归地用这种一分为二的策略对选手进行分割,直到只剩下两个选手时,日程表的制定就很简单了。两个选手进行比赛就可以了。
3.3代码实现
#include <iostream> #include <cstdio> using namespace std; int a[10000][10000];//定义数组表示选手在第i个选手在第j天遇到的选手 void table(int k, int n) { for(int i = 1; i <= n; i ++) { a[1][i] = i; } int m = 1; for(int s = 1; s <= k; s++)//将问题分成了几个部分 { n/=2;//每次都将问题一分为二 for(int t = 1; t <= n; t++)//再对每个部分进行划分 for(int i = m+1; i <= 2*m; i++) for(int j = m+1; j <= 2*m; j++) { a[i][j+(t-1)*m*2] = a[i-m][j+(t-1)*m*2-m];//将左上角小块中的所有数字按其相对位置抄到右下角 a[i][j+(t-1)*m*2-m] = a[i-m][j+(t-1)*m*2];//将左下角小块中的所有数字按其相对位置抄到右上角 //printf("i = %d\t j+(t-1)*m*2 = %d\t j+(t-1)*m*2-m = %d\t, i-m=%d\n", i, j+(t-1)*m*2, j+(t-1)*m*2-m, i-m); } m *= 2;//逐步扩大复制块,递归地解决问题 } } int main() { int k; cin >> k; int n = 1; for(int i = 1; i <= k; i++) n *= 2; table(k, n); for(int i = 1; i <= n; i ++) { for(int j = 1; j <= n; j ++) { printf("%d%c", a[i][j], j!=n?' ':'\n'); } } return 0; }