你所在的年级有5个班,每班一支球队在同一块场地上进行单循环赛, 共要进行10场比赛. 如何安排赛程使对各队来说都尽量公平呢. 下面是随便安排的一个赛程: 记5支球队为A, B, C, D, E,在下表左半部分的右上三角的10个空格中, 随手填上1,2,10, 就得到一个赛程, 即第1场A对B, 第2场B对C, , 第10场C对E. 为方便起见将这些数字沿对角线对称地填入左下三角.
这个赛程的公平性如何呢, 不妨只看看各队每两场比赛中间得到的休整时间是否均等. 表的右半部分是各队每两场比赛间相隔的场次数, 显然这个赛程对A, E有利, 对D则不公平.
从上面的例子出发讨论以下问题:
1)对于5支球队的比赛, 给出一个各队每两场比赛中间都至少相隔一场的赛程.
2)当n支球队比赛时, 各队每两场比赛中间相隔的场次数的上限是多少.
3)在达到2) 的上限的条件下, 给出n=8, n=9的赛程, 并说明它们的编制过程.
问题一
问题二
偶数个球队:各队每两场比赛中间相隔的场次数的最小值的上限是 n 2 − 1 \dfrac{n}{2}-1 2n−1
奇数个球队:各队每两场比赛中间相隔的场次数的最小值的上限是 n − 3 2 \dfrac{n-3}{2} 2n−3
向下取整后,不分奇偶,都是 n − 3 2 \dfrac{n-3}{2} 2n−3
式子来源: R ≤ ( n − 3 ) / 2 R\le (n-3)/2 R≤(n−3)/2, R R R为间隔
问题三
编制过程:(暂不给予说明)
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 20;
int n,r,nx,mr;
vector<pair<int,int> >v(200);
vector<int>chose(200);//选择
int interval[N];//间隔
int ans[N][N];//答案矩阵//优化结构
map<pair<int,int>,int>mv;//对局->选择
int maxn[N];//当前球队比赛场次最大值(球队上次比赛的场次)void f(int);
inline void print_ans();int main(){ // n=12 运行时间突变cin >> n; // n个球队r = (n-3)/2;//间隔下线(下限)nx = (n-1)*n/2;//(n-1)+(n-2)+...+1//step1. 列出每一个对局(小数在前),并初始化对局int x = 1;for(int i=1;i<=n;i++){for(int j=i+1;j<=n;j++){v[x++] = make_pair(i,j);mv[{i,j}]=x-1;}}for(int i=1;i<=n;i++){interval[i] = -1;maxn[i] = -1;}//step2. 初始化选择if(n%2==0){mr = r+3;//间隔上限int k = 1;//12 34 ... n-1 nfor(int i=1;i<=n-1;i+=2){ans[i][i+1] = ans[i+1][i] = (i+1)/2;maxn[i]=maxn[i+1] = k;chose[k++]=mv[{i,i+1}];}for(int i=1;i<=n-5;i+=4){for(int j=0;j<2;j++){//ans[i+j][i+j+2] = ans[i+j+2][i+j] = k;ans[i+j][(i+j+2+n)%n] = ans[(i+j+2+n)%n][i+j] = k;maxn[i+j]=maxn[(i+j+2+n)%n] = k;chose[k++]=mv[{i+j,(i+j+2+n)%n}];}}f(k);}else{mr = r+2;//间隔上限int k = 1;//12 34 ... n-2 n-1for(int i=1;i<=n-2;i+=2){ans[i][i+1] = ans[i+1][i] = (i+1)/2;maxn[i]=maxn[i+1] = k;chose[k++]=mv[{i,i+1}];}f(k);}//print_ans();return 0;
}
bool flag = false;
void f(int x){if(flag || x == nx+1){if(!flag)print_ans();flag = true;//all return//cout ansreturn ;}//step3. 算间隔,取出必选(可选)的列表(对局列表)vector<int>must_chose;
#if 0for(int i=1;i<=r+1;i++){// x x-1 x-2 .. x-rpair<int,int> pi_t = v[chose[x-i]];interval[pi_t.first] = interval[pi_t.second] = i;}
#endifbool m_or_ke = false;//false 表示 是可选列表, 反之是必选vector<int>must_n;vector<int>no_n;for(int i=1;i<=n;i++){if(maxn[i]==-1){//没被选过must_n.push_back(i);continue;}if(m_or_ke){//必选已经出来了if(x-maxn[i]>mr)must_n.push_back(i);//直接加必选if(x-maxn[i]<=r)no_n.push_back(i);//不可选continue;}if(x-maxn[i]>mr){if(!m_or_ke)must_n.clear();m_or_ke = true;must_n.push_back(i);}else if(x-maxn[i]>r){must_n.push_back(i);}else{no_n.push_back(i);//不可选}}//预处理bool mn_[n+1];for(int i=1;i<=n;i++)mn_[i]=false;for(auto xx:must_n)mn_[xx]=true;for(auto xx:no_n)mn_[xx]=false;//must_n -> must_chose//for(int i=1;i<=nx;i++){//可优化// if(mn_[v[i].first]&&mn_[v[i].second])must_chose.push_back(i);//}for(int i=1;i<=n;i++){//优化后if(!mn_[i])continue;for(int j=i+1;j<=n;j++){if(mn_[j]){must_chose.push_back(mv[{i,j}]);}}}int mi = must_chose.size();for(int i=0;i<mi;i++){//checkif(ans[v[must_chose[i]].first][v[must_chose[i]].second])continue;//chosechose[x] = must_chose[i];ans[v[must_chose[i]].first][v[must_chose[i]].second] = x;ans[v[must_chose[i]].second][v[must_chose[i]].first] = x;int vf_maxn = maxn[v[must_chose[i]].first];//备份int vs_maxn = maxn[v[must_chose[i]].second];maxn[v[must_chose[i]].first] = maxn[v[must_chose[i]].second] = x;f(x+1);//cut choseans[v[must_chose[i]].first][v[must_chose[i]].second] = 0;maxn[v[must_chose[i]].first] = vf_maxn;maxn[v[must_chose[i]].second] = vs_maxn;}
}
inline void print_ans(){for(int i=1;i<=n;i++){vector<int>v1;for(int j=1;j<=n;j++){cout << ans[i][j] << ' ';v1.push_back(ans[i][j]);}sort(v1.begin(),v1.end());cout << ',';for(int j=2;j<n;j++){cout << v1[j]-v1[j-1]-1 << ' ';}cout << '\n';}return ;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)cout << ans[i][j] << ' ';cout << '\n';}
}
代入n=9 / n=8答案可出