循环赛日程安排分治法分析
问题描述:
- 设有n(n = 2^k)位选手参加网球循环赛,循环赛共进行n-1天,每位选手要与其他n-1位选手比赛一场,且每位选手每天必须比赛一场,不能轮空。试按此要求为比赛安排日程。
编程思想:
假设n位选手被顺序编号为1,2,3,…,n,比赛的日程表是一个n行n-1列的表格,i行j列的表格内容是第i号选手在第j天的比赛对手。根据分而治之的原则,可从其中一半选手(2^(n-1位)的比赛日程,导出全体n位选手的日程,最终细分到只有两位选手的比赛日程出发。可假设只有8位选手参赛,若1至4号选手之间的比赛日程填在日程表的左上角(4行3列),5至8号选手之间的比赛日程填在日程表的左下角(4行3列);那么左下角的内容可由左上角的对应项加上数字4得到。至此,剩余的右上角(4行4列)是为编号小的1至4号选手与编号大的5至8号选手之间的比赛日程安排。例如,在第4天,让1至4号选手分别与5至8号选手比赛,以后各天,依次由前一天的日程安排,让5至8号选手“循环轮转”即可。最后,比赛日程表的右下角的比赛日程表可由,右上角的对应项减去数字4得到。
具体实现:
/** * @brief 循环赛日程安排,分治法 * * @param[in] A 数组指针 * @param[in] k 选手数幂,如2^k位选手 */ void GameTable(int** A, int k) { int n = 2; A[0][0] = 1; A[0][1] = 2; A[1][0] = 2; A[1][1] = 1; for (int t = 1; t < k; ++t) { int temp = n; n *= 2; int i = 0, j = 0; //左下角 for (i = temp; i < n; ++i) { for (j = 0; j < temp; ++j) { A[i][j] = A[i - temp][j] + temp; } } //右上角 for (i = 0; i < temp; ++i) { for (j = temp; j < n; ++j) { A[i][j] = A[i + temp][j - temp]; } } //右下角 for (i = temp; i < n; ++i) { for (j = temp; j < n; ++j) { A[i][j] = A[i - temp][j - temp]; } } } } int main() { float k = 3.0f; int personCount = pow(2.0f, k); int** A = new int*[personCount]; for (int t = 0; t < personCount; ++t) { A[t] = new int[personCount]; } GameTable(A, k); for (int i = 0; i < personCount; ++i) { for (int j = 0; j < personCount; ++j) { cout << A[i][j] << " "; } cout << endl; } }
执行结果图: