問題描述:
設有n=2k個運動員要進行網球迴圈賽。現要設計一個滿足以下要求的比賽日程表:
- 每個選手必須與其他n-1個選手各賽一次;
- 每個選手一天只能參賽一次;
- 迴圈賽在n-1天內結束。
請按此要求將比賽日程表設計成有n行和n-1列的一個表。在表中的第i行,第j列處填入第i個選手在第j天所遇到的選手。其中1≤i≤n,1≤j≤n-1。
-------------------------------------------------------------------------
解答:
這個演算法被劃分到了分治演算法的例子,的確它包含分治演算法的意思,但是如果從構建規則出發,而不是從分治思想出發,我怎麼感覺想起來更不繞彎呢!
其實這個表的構建很容易。首先有個陣列T,n行n列,第一列第一行填入數字1,初始就是這個樣子的。
1 |
… |
… |
… |
此時k=0,由它向下擴充套件,向右擴充套件,向右下擴充套件就變成了k=1的情況,即由一行一列變成二行而列,如何擴充套件呢?
向下:原始值(左上角)加1(k)向下搬
右上:把剛才填充上的左下的搬上去
右下:把原來的,左上的搬下去。
1 |
2 |
|
2 |
1 |
|
按照這個規律,當K=2的時候,應該是這個樣子的
1 |
2 |
3 |
4 |
|
2 |
1 |
4 |
3 |
|
3 |
4 |
1 |
2 |
|
4 |
3 |
2 |
1 |
|
當K=3的時候,應該是這個樣子的
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
2 |
1 |
4 |
3 |
6 |
5 |
8 |
7 |
3 |
4 |
1 |
2 |
7 |
8 |
6 |
6 |
4 |
3 |
2 |
1 |
8 |
7 |
5 |
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 |
把第一列去掉就是我們想要的8個運動員的比賽日程表。
想通了,程式碼就非常容易了。
/// <summary> /// 生成比賽日程表 /// </summary> /// <param name="k">運動員數為2的n次方</param> public void GenerateTable(int n) { int N=(int)Math.Pow(2,n); int[,] T=new int[N,N]; T[0, 0] = 1; for (int k = 0; k < n; k++) { int end=(int)Math.Pow(2,k)-1; //左上小方塊結束的行和列 int end2 = (int)Math.Pow(2,k+1) - 1; //本次填充結束的行和列 int baseNum = (int)Math.Pow(2,k); //左下填充 for (int i = end + 1; i <= end2; i++) { for (int j = 0; j <= end; j++) { T[i, j] = T[i - baseNum, j] + baseNum; } } //右上填充 for (int i = 0; i <= end; i++) { for (int j = end + 1; j <= end2; j++) { T[i, j] = T[i + baseNum, j - baseNum]; } } //右下填充 for (int i = end + 1; i <= end2; i++) { for (int j = end + 1; j <= end2; j++) { T[i,j]=T[i-baseNum,j-baseNum]; } } } //output StringBuilder sb = new StringBuilder(); for (int i = 0; i < N; i++) { for (int j = 1; j < N; j++) { sb.Append(T[i,j]+" "); } sb.Append("\n"); } Console.WriteLine(sb); Console.ReadKey(); }