最近写一个程序需要用到循环赛程生成的功能。如果球队的数目为2的n次方的话,用分治法就比较简单, 但是像6、20这样的数目,分解下来出现3、5的数目就比较难办。
将球队编号0...n-1, 画了一个矩阵来排出赛表后,想到可以编程实现这种直观的方法,来对赛程进行安排。
已6只球队为例,形成6x6矩阵。排第一轮时,从第一行开始,填写左上角的矩阵,A对B填1,然后第1行,第1列,第2行,第2列均不可选择。于是B行不再处理,然后到第C,可以尝试第一个空的C对D。万一C对D被占用之后,剩下的E和F不能对阵(比如已经对阵过了)(当然这个例子中不是这样子,但我们假设嘛),然后C对D,不成立,继续尝试C对E。(此段描述见下图)
A B C D E F A B C D E F
A A -- 1
B B -- x x x x
C C --
D D --
E E --
F F --
赛程一共n-1轮,每轮 n/2场比赛,照上一段的方式,可以使用递归实现穷举。
代码入下:
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class MatchSchedule {
private int table[][];
private int roundNum;
private int roundMatchNum;
private int teamCount;
public boolean generate(int teamCount){
if (0 != (teamCount % 2)){
return false;
}
this.teamCount = teamCount;
roundNum = teamCount - 1;
roundMatchNum = teamCount / 2;
table = new int[teamCount][teamCount];
for (int i = 0; i < teamCount; i++){
for (int j = 0; j < teamCount; j++){
table[i][j] = -1;
}
}
return genRound(1);
}
public void printSchedule(){
for (int i = 1; i <= roundNum; i++ ){
System.out.println("*******************");
System.out.println("第" + i + "轮");
for (int m = 0; m < teamCount; m++){
for (int n = m+1; n < teamCount; n++){
if (table[m][n] == i){
System.out.println(m + " VS " + n);
}
}
}
}
}
private boolean genRound(int round){
List<Integer> occupies = new ArrayList<Integer>();
if (genMatch(round, 1, occupies)){
return true;
}
return false;
}
private boolean genMatch(int round, int match, List<Integer> occupies){
System.out.println("generate round " + round + " match " + match);
for (int i = 0; i < teamCount; i++){
if (find(occupies, i)){
continue;
}
for (int j = i + 1; j < teamCount; j++){
if (find(occupies, j)){
continue;
}
if (-1 != table[i][j]){
continue;
}
table[i][j] = round;
if ((match == roundMatchNum) && (round == roundNum )){
return true;
}
occupies.add(i);
occupies.add(j);
boolean nextStepRst;
if (match == roundMatchNum){
nextStepRst = genRound(round+1);
}else{
nextStepRst = genMatch(round, match+1, occupies);
}
if (true == nextStepRst){
return true;
}else{
occupies.remove(occupies.size()-1);
occupies.remove(occupies.size()-1);
table[i][j] = -1;
}
}
}
return false;
}
private boolean find(List<Integer> occupies, int v){
for (int i = 0; i < occupies.size(); i++){
if (occupies.get(i) == v){
return true;
}
}
return false;
}
public static void main(String args[]){
System.out.print("Team number:");
int input = Integer.parseInt(args[0]);
MatchSchedule schedule = new MatchSchedule();
if (false == schedule.generate(input)){
System.out.println(input + "支球队不能生成赛程");
}else{
System.out.println(input + "支队的赛程如下");
schedule.printSchedule();
}
}
}
当n比较大时,此算法的时间性能和对栈空间的占用都成问题。
实际生活中,n往往是固定的, 其实可以准备一次赛程表,换换A、B、C、D对应的队伍,就可以长期使用了。