比赛日程安排
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
摘要:对于单循环赛的比赛日程安排问题,利用分治算法给出了可读性较好的设计,并详细分析了各种假设下的时间复杂度。
中国论文网 /8/view-46102.ht
关键词:分治算法;复杂度;递归;循环赛
中图分类号:TP301.6文献标识码:A
任何一个可以用计算机求解的问题所需的计算时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。分治法是计算机科学中经常使用的一种算法。设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。但是不是所有问题都适合用分治法解决。当求解一个输入规模为n且取值又相当大的问题时,使用蛮力策略效率一般得不到保证。
1 分治法应用条件及一般步骤
1.1分治法的应用条件
若问题能满足以下所列条件,就可以考虑用分治法来提高解决问题的效率。
(1)能将这n个数据分解成k个不同子集合,且得到的k个子集合是可以独立求解的子问题,其中1 (2)分解所得到的子问题与原问题具有相似的结构,便于利用递归或循环机制; (3)合并各个子问题的解,就是原问题的解。 1.2分治法的一般步骤 采用分治算法解决问题的基本步骤为: (10分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题; (2)求解子问题:若子问题规模较小而容易被解决则直接解,否则再继续分解为更小的子问题,直到容易解决; (3)合并:将已求解的各个子问题的解,合并为原问题的解。 需要注意的是,不是所有的分治法都比简单的蛮力法更有效。 2 循环赛分治算法 下面,我们应用分治法解决循环赛日程表的设计问题。 2.1问题描述 有n支球队参加循环赛,设计一个满足下面要求的比赛日程表: (1)每支球队必须与其他n-1支球队各赛一次; (2)每支球队一天只能比赛一次; (3)当n为偶数时,比赛进行n-1天;当n为奇数时,比赛进行n天。 按此要求,可将比赛日程表设计成一个n行n列的二维表。 2.2算法分析 当n=2k(k=1、2、3、4,……,n=2、4、8、16,……)时,此时问题比较简单。按照分治的策略,可将所有参赛的选手分为两部分,n=2k个选手的比赛日程表就可以通过为n/2=2k-1个选手设计的比赛日程表来决定。递归地执行这种分割,直到只剩下2个选手时,比赛日程表的制定就变得很简单:只要让这2个选手进行比赛就可以了。再逐步合并子问题的解即可得到原问题的解。 算法如下: void tourna(int n)//基本的分治算法 { if(n==1){a[0][0]=1;return;} tourna(n/2);//分治 copy(n); //合并 } void copy(int n) { int m=n/2; for(int i=0;i for(int j=0;j { //由左上角小块的值算出对应的右上角小块的值 a[i][j+m]=a[i][j]+m; //由右上角小块的值算出对应的左下角小块的值 a[i+m][j]=a[i][j+m]; //由左上角小块的值算出对应的右下角小块的值 a[i+m][j+m]=a[i][j]; } } 分析算法的时间性能,迭代处理的循环体内部有2个循环语句,基本语句是最内层循环体的赋值语句,即填写比赛日程表中的元素。基本语句的执行次数是:T(n)=T(n)3-0(4), 所以算法的时间复杂度为0(4)。 下面推广n为任意整数: 当n小于或等于1时,没有比赛。 当n是偶数时,至少举行n-1轮比赛. 当n是奇数时,至少举行n轮比赛,这时每轮必有一支球队轮空。 为了统一奇数偶数的不一致性,当n为奇数时,可以加入第n+1支球队(虚拟球队,实际上不存在),并按n+1支球队参加比赛的情形安排比赛日程。那么n(n 为奇数)支球队时的比赛日程安排和n+1支球队时的比赛日程安排是一样的。只不过每次和n+1队比赛的球队都轮空。所以,我们只需考虑n为偶数时情况(以上问题的详细证明,见《初等数论》-北京大学出版社-潘承洞)。 下面仅对n为偶数进行讨论: (1)当n/2为偶数时,与n= 情形类似,可用分治法求解。 (2)当n/2为奇数时,递归返回的轮空的比赛要作进一步处理。可以在前n/2轮比赛中让轮空选手与下一个未参赛选手进行比赛。 算法如下: void tourna(int n)//改进的分治赛算法 { if(n==1){a[0][0]=0;return;} if(odd(n)){tourna(n+1);return;} //n为奇数,分治tourna(n/2);//n为偶数,分治 makecopy(n); //合并 } bool odd(int n)//判断n奇偶性 { return n&1; //n为奇数,返回1,n为偶数,返回0 } void makecopy(int n)//合并算法 { if((n/2)>1&&odd(n/2)) copyodd(n); //n/2为奇数时 else copy(n); } void copyodd(int n) // n/2为奇数时的合并算法{ int m=n/2; for(int i=0;i { b[i]=m+i; b[m+i]=b[i]; } for(i=0;i { //由左上角小块的值算出相应的左下角小块的值 for(int j=0;j { if(a[i][j]>=m) { a[i][j]=b[i];