问题分析
循环赛问题属于分治思想的经典问题,主要先对最小模块初始化,通过递归,分治进行处理得到最终的赛程表。对于队伍数目并不是刚好为2的整数幂的情况,也做了相应的处理,但是并不是所有情况均可完美解决。
算法解析
①赛程表求解:分治思想
1)首先对左上角第一个最小模块进行初始化
if(index==0){
dp[0][0]=1;
dp[0][1]=2;
dp[1][1]=1;
dp[1][0]=2;
index++;
arrange(teamNum,index);
}
2)开始二分法递归依次处理左下角,右下角,右上角。第一轮以初始化最小模块为基本单元,首先将该模块左下角,右下角,右上角的同规模单元填充赋值。填充完毕后以四块单元组成的区域为新的最小模块,即下一轮递归认为左上角的模块已经完成装填,继续递归调用,直到index二分的指数下的以二为底的整数幂大于队伍数目,结束递归。
else{
int size=0x1<<index;
int half = size/2;
//左下角利用初始化最小模块赋值
for(int i=0;i<half;i++){
for (int j=0;j<half;j++){
dp[i+half][j] = dp[i][j]+half;
}
}
//右上角赋值
for(int i=0;i<half;i++){
for (int j=0;j<half;j++){
dp[i][j+half] = dp[i+half][j];
}
}
//右下角赋值
for(int i=0;i<half;i++){
for (int j=0;j<half;j++){
dp[i+half][j+half] = dp[i][j];
}
}
if(size>=teamNum){
return;
}
//每次递归完成后默认完成了一个左上角模块的装填
arrange(teamNum,index+1);
}
②将结果处理成n支队伍数目:如果是队伍匹配到大于队伍总数目的队号需要安排轮空。
public void print(){
for (int i=1;i< (0x1<< index);i++){//控制列号
System.out.println("第"+i+"天赛程:");
for(int j=0;j< teamNum;j++){
if(dp[i][j]<=teamNum){
System.out.print(dp[j][0]+"对阵"+dp[i][j]+" ");
}
else{
System.out.print(dp[j][0]+"轮空 ");
}
}
System.out.println();
}
}
③结果展示
完整代码
package com.algorithm.day1;
import java.util.Scanner;
public class SportSchedule {
int [][] dp =new int[100][100];
int index=0;//以二为底的幂指数
int teamNum=0;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
SportSchedule sportSchedule = new SportSchedule();
sportSchedule.teamNum = scanner.nextInt();
int tmp = sportSchedule.teamNum;
while(tmp>0){
tmp=tmp>>1;
sportSchedule.index++;//位运算计算指数index
}
if ((0x1<<sportSchedule.index)/ sportSchedule.teamNum==2){
sportSchedule.index--;//恰好为2的整数幂时,index需要减一
}
sportSchedule.arrange(sportSchedule.teamNum, 0);
sportSchedule.print();
}
public void arrange(int teamNum,int index){
if(index==0){
dp[0][0]=1;
dp[0][1]=2;
dp[1][1]=1;
dp[1][0]=2;
index++;
arrange(teamNum,index);
}
else{
int size=0x1<<index;
int half = size/2;
//左下角利用初始化最小模块赋值
for(int i=0;i<half;i++){
for (int j=0;j<half;j++){
dp[i+half][j] = dp[i][j]+half;
}
}
//右上角赋值
for(int i=0;i<half;i++){
for (int j=0;j<half;j++){
dp[i][j+half] = dp[i+half][j];
}
}
//右下角赋值
for(int i=0;i<half;i++){
for (int j=0;j<half;j++){
dp[i+half][j+half] = dp[i][j];
}
}
if(size>=teamNum){
return;
}
//每次递归完成后默认完成了一个左上角模块的装填
arrange(teamNum,index+1);
}
}
public void print(){
for (int i=1;i< (0x1<< index);i++){//控制列号
System.out.println("第"+i+"天赛程:");
for(int j=0;j< teamNum;j++){
if(dp[i][j]<=teamNum){
System.out.print(dp[j][0]+"对阵"+dp[i][j]+" ");
}
else{
System.out.print(dp[j][0]+"轮空 ");
}
}
System.out.println();
}
}
}