循环赛日程表问题

bessiezhang 2002-12-29 02:13:51
设有n个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:
1.每个选手必须与其他n-1个选手各赛一次;
2.每个选手一天只能参赛一次;
3.N是偶数时,循环赛在n-1天内结束。N是基数时,循环赛进行N天.
请按此要求将比赛日程表设计成有n行和n-1列的一个表。在表中的第i行,第j列处填入第i个选手在第j天所遇到的选手。其中1≤i≤n,1≤j≤n-1。
当N等于2的K次方时,用分治法容易求出,现在没有这个条件了,小女子就想不出来了,请各位高手帮帮忙啊.

...全文
890 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
gruse 2003-03-04
  • 打赏
  • 举报
回复
同意楼上的。
xilivivian 2003-03-04
  • 打赏
  • 举报
回复
我将球队号排成一个队列:1,2,3,......
当n是偶数时:第一天:队列的第一个球队与第n个球队比赛,第二个球队与第n-1球队比赛,......
第二天:将队列循环右移一位后,还按第一天那样排;
当n是奇数时:第一天:队列的第一个球队与第n-1个球队比赛,第二个球队与第n-2球队比赛,......
第二天:将队列循环右移一位后,还按第一天那样排:
Wenxy1 2003-02-28
  • 打赏
  • 举报
回复
请写你的上面这个程式的算法。
xilivivian 2003-02-28
  • 打赏
  • 举报
回复
#include<iostream.h>


void gishu(int n);
void youshu(int n);

void main()
{int a;
cout<<"请输入N的值:"<<endl;
cin>>a;
if(a%2==0)
youshu(a);
else gishu(a);
}


void youshu( int n)
{int a[2500]={0};
int b[50]={0};
for(int g=0;g<n;g++)
b[g]=g+1;
for(int i=0;i<n;i++)
{int e=b[0];
for(int j=0;j<(n-2);j++)
b[j]=b[j+1];
b[j]=e;
for(int m=0;m<n/2;m++)
{int c=b[m];
int d=b[n-m-1];
a[i*n+c-1]=d;
a[i*n+d-1]=c;
}

}
for(int t=0;t<n-1;t++)
{for(int j=0;j<n;j++)
cout<<a[t*n+j]<<'\0'<<',';
cout<<'\n';
}
}

void gishu(int n)
{ int b[50]={0};
int a[2500]={0};
for(int g=0;g<n;g++)
b[g]=g+1;
for(int i=0;i<n;i++)
{
int e=b[0];
for(int j=0;j<(n-1);j++)
b[j]=b[j+1];
b[j]=e;

for(int m=0;m<(n-1)/2;m++)
{int c=b[m];
int d=b[n-m-2];
a[i*n+c-1]=d;
a[i*n+d-1]=c;}
}
for(int t=0;t<n;t++)
{for(int j=0;j<n;j++)
cout<<a[t*n+j]<<',';
cout<<'\n';
}
}
我已将两个程序合并成了一个程序了。也已运行成功。
但是n的最大值是50;
xilivivian 2003-02-27
  • 打赏
  • 举报
回复
上贴能运行成功,各位有何意见请发贴。
xilivivian 2003-02-27
  • 打赏
  • 举报
回复
#include<iostream.h>
#define N 11
void main()
{ int b[N]={0};
int a[N][N]={0};
for(int g=0;g<N;g++)
b[g]=g+1;
for(int i=0;i<N;i++)
{
int e=b[0];
for(int j=0;j<(N-1);j++)
b[j]=b[j+1];
b[j]=e;

for(int m=0;m<(N-1)/2;m++)
{int c=b[m];
int d=b[N-m-2];
a[i][c-1]=d;
a[i][d-1]=c;}
}
for(int t=0;t<N;t++)
{for(int j=0;j<N;j++)
cout<<a[t][j]<<',';
cout<<'\n';
}
}

#include<iostream.h>
#define N 10
void main()
{int a[N][N]={0};
int b[N]={0};
for(int g=0;g<N;g++)
b[g]=g+1;
for(int i=0;i<N;i++)
{int e=b[0];
for(int j=0;j<(N-2);j++)
b[j]=b[j+1];
b[j]=e;
for(int m=0;m<5;m++)
{int c=b[m];
int d=b[N-m-1];
a[i][c-1]=d;
a[i][d-1]=c;
}

}
for(int t=0;t<N-1;t++)
{for(int j=0;j<N;j++)
cout<<a[t][j]<<'\0'<<',';
cout<<'\n';
}
}

我写了两个主程序分别为N=奇数(N=11,上面一个程序),N=偶数(N=10,下面一个程序)
其中N可以换其它的值。由于定义数组不能用参数。所以我不能写成一个程序。如果各位有什么办法能把两个程序合为一个程序麻烦回一贴。

rushman 2002-12-31
  • 打赏
  • 举报
回复
首先,奇数/偶数运动员并没有本质区别(如为奇数,虚拟一个凑成偶数,和它对阵的当成轮空)。因而,在任何情况下,都可以将运动员分成上下两个半区。
先安排上半区区内的比赛(在此递归,直到只剩两名选手),参照上半区的比赛,安排线半区的比赛(就是将尚未安排比赛的平移过来)。
安排下半区比赛,根据选手号,一次错开就行了。因为有虚拟选手,所以,轮空一并进行了安排。
但是,虚拟选手在赛程的安排上还是又影响的(否则,半区内的比赛就没有轮空了,实际上隐含的已经安排了)。我把虚拟选手的编号叫做敏感数或敏感值,当遇到对手号是敏感数时,而且,非递归顶层的时候,此时,敏感数相对的选手实际上是存在的,需要把它的比赛调开。

//-----------------------------------------------------------
//考虑这个问题差不多化了我一整天的时间,看到结果时--高兴!
//前面给的程序是错的(为了这个错误,花了几个小时:().
//我先写了个穷举算法,发现问题能解决,但是太慢,几乎是指数的指数
//回到这个问题,仔细考虑了一下,这个问题有好多解,只要找一个容易的就行了
//接着,发现分成半区是个好方法,只要将区内的比赛安排好,
//区间的比赛很容易安排,无形中减小了问题空间,解决了虚拟选手的问题后
//形成了下面的算法。
//
//下面的程序,我简单的测了一下(实在没时间,否则昨天就贴出来了),
//没有大的问题(此算法没有根本上的错误)
//------------------------------------------------------
#include <iostream>

#ifdef _DEBUG
#undef _DEBUG
#include <conio.h>
#endif
using namespace std;


//--------------------------------------------------------------
struct althete_info{
int a_count;
int days;
int * tbl;

althete_info():a_count(0),days(0),tbl(0){}
~althete_info(){init(0);}

int * init(int ac);
int check_match(int a1,int a2,int day);
int set_match(int a1,int a2,int day);
void list(void);
int build(int a_range,int u_sensitive);
int &unit(int althete,int day){return (tbl[althete * days + day]);}
};

int * althete_info::init(int ac)
{
a_count = ac;
days = ac - 1;
days |= 1;//天数总是单数,
delete [] tbl;
if(a_count > 0)
{
tbl = new int[(a_count + 1) * days];
if(tbl == NULL)
{
cout<<"Out of Memory !"<<endl;
exit(0);
}
// 对于填充 -1 ,单字节与整字没有区别
memset(tbl,-1,((a_count + 1) * days) * sizeof(int));
}
return(tbl);
}

//--------------------------------------------------------------

void althete_info::list(void)
{
int i,j;

cout<<endl<<"=========";
for(i = 0;i < days;i++)
cout<<"====";
cout<<endl;
cout<<"Day ";
for(i = 0;i < days;i++)
{
cout.width(4);
cout<<i;
}
cout<<endl<<"---------";
for(i = 0;i < days;i++)
cout<<"----";
cout<<endl;
for(i = 0;i < a_count;i++)
{
cout<<"No.";
cout.width(2);
cout<<i<<" : ";
for(j = 0;j < days;j++)
{
cout.width(4);
#ifndef _DEBUG
if(tbl[days * i + j] < 0 || unit(i,j) >= a_count)
cout<<'-';
else
#endif
cout<<tbl[days * i + j];
}
cout<<endl;
}
}

//----------------------------------------------------------
//判断 a2 选对手是否与 a1 比赛过,
//同时 a2 当天有无比赛
//没有比赛过,同时当天无比赛,返回 1,否则 0
int althete_info::check_match(int a1,int a2,int day)
{
int i;
int resu = 1;
// 检查两人是否赛过
for(i = 0;i < day;i++)
{
if(tbl[a1 * days + i] == a2)
resu &= 0;
}
// 检查 a2 当天是否有比赛
// 应基于前面的数据
for(i = day;i < a1 * days + day;i += days)
{
if(tbl[i] == a2)
resu &= 0;
}

return(resu);
}

int althete_info::set_match(int a1,int a2,int day)
{
#ifdef _DEBUG
if(a1 > a_count || a2 > a_count || a1 < 0 || a2 < 0)
{
cout<<"Error : out of range"<<endl;
cout<<"a1 = "<<a1<<"\ta2 = "<<a2<<"\tday = "<<day<<endl;
getch();
}
#endif

if(check_match(a1,a2,day) == 0) return (0);

if(a1 >= 0 && a1 <= a_count)
tbl[a1 * days + day] = a2;
if(a2 >= 0 && a2 <= a_count)
tbl[a2 * days + day] = a1;
return (1);
}

//--------------------------------------------------------------
// 递归填赛程表:非穷举法
// 完整处理当前赛区的日程安排,并将影响扩散到有效范围。
//--------------------
// 算法:递归
// 如果本区选手只有两人,安排比赛并返回
// 如果本区选手数为奇,加一个虚拟选手,将虚拟选手号设为敏感值
// 将本区选手分为上下两个半区,
// 安排上半区选手的区内比赛
// 安排两区之间的比赛
// 参照上半区比赛,安排下半区的比赛
//--------------------
// Params :
// a_range : 范围,序号为 0 到 a_range-1 的选手
// u_sensitive : 上一级的敏感编号,如遇此编号,需转换
//--------------------
// Return :
// 本半区选手数的奇偶性,也表示对另半区选手已经安排过一场比赛
// 0:偶数,未安排比赛
// 1:奇数,已安排一轮
//--------------------------------------------------------------
int althete_info::build(int a_range,int u_sensitive)
{
// 如果本区选手只有两人,安排比赛并返回
if(a_range == 2)
{
set_match(0,1,0);
#ifdef _DEBUG
list();
getch();
#endif
return(0);
}

int c_sensitive;//本区的敏感号
int ac_comp;//本区计算选手数,处理奇偶问题
int a_hrange;//半区选手数
int hr_resu;//上半区结果,调整下半区赛程
int h_days;//上半区比赛天数
int cr_resu;//本区结果,返回值
int ti,tj,tk;

// 如果本区选手数为奇,加一个虚拟选手,将虚拟选手号设为敏感值
ac_comp = (a_range + 1) & 0xFFFFFFFE;
cr_resu = (a_range & 1);
if(ac_comp == a_range)
c_sensitive = -1;
else
c_sensitive = a_range;//a_range == ac_comp - 1,即最后一个虚拟选手号
// 将本区选手分为上下两个半区,
a_hrange = ac_comp / 2;
h_days = (a_hrange - 1) | 1;
// 安排上半区选手的区内比赛
hr_resu = build(a_hrange,c_sensitive);
// 安排两区之间的比赛,实际选手
// 只针对上半区,下半区不用单独设置
for(ti = 0;ti < a_hrange;ti++)
{
// ti 的第 tj 个对手,如果上半区已安排过一轮,跳过首轮
for(tj = hr_resu;tj < a_hrange ;tj++)
{
tk = a_hrange + (ti + tj) % a_hrange;
if(tk == c_sensitive && a_range < a_count)//调整本区敏感数
tk += ti;
if(tk == u_sensitive && a_range < a_count)//调整上一级的敏感数
tk += ti;
set_match(ti,tk,tj + h_days - hr_resu);
}
}
// 参照上半区比赛,安排下半区的比赛
for(ti = 0;ti < (a_hrange - cr_resu);ti++)
{
for(tj = 0;tj < h_days;tj++)
{
//该选手当天没比赛
if(unit(a_hrange + ti,tj) < 0)
{
tk = unit(ti,tj) + a_hrange;
if(tk == c_sensitive && a_range < a_count)//调整本区敏感数
tk += a_hrange + ti;
if(tk == u_sensitive && a_range < a_count)//调整上一级的敏感数
tk += a_hrange + ti;

set_match(ti + a_hrange,tk,tj);
}
}
}
#ifdef _DEBUG
list();
getch();
#endif
return(cr_resu);
}

//--------------------------------------------------------------

void main(void)
{
althete_info as;
int i;
char c;
while(1)
{
cin>>i;
cin.clear();
do{
cin.get(c);
}while(c > 10);
if(i <= 1)
return;
as.init(i);
cout<<"安排比赛场次......"<<endl;
as.build(as.a_count,-1);
as.list();
}
}
Wenxy1 2002-12-30
  • 打赏
  • 举报
回复
接我的上个帖子。
经证明,可以得出个结论,各个队员每两场比赛中间相间隔的比赛场次数的上限是,(N-3)/2的不在于取整{其结果为不于(N-3)/2的最大整数}。
比赛轮数: ,N为奇数,比赛轮数为 N ;N为偶数,比赛轮数为 N-1.一轮比赛可以这样理解:用轮转法,每变换一次得到的比赛日程.

若N为偶数,有队员轮空的情况出现,用上述轮转法经过一定的修改可以得出.
rushman 2002-12-29
  • 打赏
  • 举报
回复
一个组合问题。
题目好像有点问题。“N是基数时,循环赛进行N天”和“有n行和n-1列”有一点矛盾。

时间来不及了,没有测试。

int n;
int days;
int * match_tbl;
//安排场次
//比过 :1
//有比赛:-1
int set_match(int a1,int a2,int day)
{
int resu = check_match(a1,a2,day);
if(resu == 0)
{
match_tbl[a1 * days + day] = a2;
match_tbl[a2 * days + day] = a1;
}
return(resu);
}

//检查是否可以安排比赛
int check_match(int a1,int a2,int day)
{
//当天是否有比赛
if(match_tbl[a1 * days + day] >= 0 || match_tbl[a2 * days + day] >= 0)
return(-1);
//是否比过
for(int i = 0;i < days;i++)
{
if(match_tbl[a1 * days + i] == a2 || match_tbl[a1 * days + i] == a2)
return(1);
}
return(0);
}

void match(void)
{
int i,j;
int curr_day;
for(i = 0;i < n;i++)
{
curr_day = 0;
for(j = 0;j < n;j++)
{
switch(set_match(i,j,curr_day)){
case -1:
case 0:
curr_day++;
break;
}
}
}

void init_match(int athlete_count)
{
n = athlete_count;
days = n - 1;
days += n & 1;
match_tbl = new int[n * days];
for(int i = 0;i < n * days;i++)
match_tbl[i] = -1;
}

void list_tbl(void)
{
for(int i = 0;i < n;i++)
{
for(int j = 0;j < days;j++)
cout<<setw(3)<<match_tbl[days * i + j);
cout<<endl;
}
}

Wenxy1 2002-12-29
  • 打赏
  • 举报
回复
:) 我不是高手,《运筹学》没学好。
Wenxy1 2002-12-29
  • 打赏
  • 举报
回复
算法:若N为偶数:(例如有8个运动员,编号:0,1,2,...7.)
0 4
1 5
2 6
3 7

就是0-4,1-5,2-6,3-7比赛.
说明:把0号固定,1到7号按逆时针方向旋转一次,得到下一个:
0 5
4 6
1 7
2 3
接着算下去....直到与未旋转前.
N为奇数,稍烦琐,你可以想想.

boyfling 2002-12-29
  • 打赏
  • 举报
回复
需要把全部的可能都算出来吗
bessiezhang 2002-12-29
  • 打赏
  • 举报
回复
:)能做出来的就行了,请这位高手指教
Wenxy1 2002-12-29
  • 打赏
  • 举报
回复
网址:mcm.edu.cn
Wenxy1 2002-12-29
  • 打赏
  • 举报
回复
对了,题里说的赛制是,单循环赛。今年10月的全国大学生数学建模竞赛里有个类似的题.网址:mcm.ecu.cn
Wenxy1 2002-12-29
  • 打赏
  • 举报
回复
一定要用分治法吗?:)
该类型的题我写过论文,算法,我有。比如用:轮转法。

24,856

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 工具平台和程序库
社区管理员
  • 工具平台和程序库社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧