一、 实验题目

设有n位选手参加网球循环赛,循环赛共进行n-1天,每位选手要与其他n-1位选手比赛一场,且每位选手每天必须比赛一场,不能轮空。试按此要求为比赛安排日程。

二、实验目的

1.深刻理解并掌握“分治算法”的设计思想;

2.提高应用“分治算法”设计技能;

3.理解这样一个观点:用递归方法编写的问题解决程序具有结构清晰,可读性强等优点,且递归算法的设计比非递归算法的设计往往要容易一些,所以当问题本身是递归定义的,或者问题所涉及到的数据结构是递归定义的,或者是问题的解决方法是递归形式的时候,往往采用递归算法来解决。

三、实验要求

1.实现《网球循环赛》问题的分治算法,并进行算法时间复杂性分析。

2.对实现的分治算法进行改进;

3.对上述改进后算法进行时间复杂性分析,通过实验结果分析对比,得出自己的结论和总结。

四、实验过程

1、算法一:

#include<stdio.h>

#define N 64

void GameTable(int k,int a[][N])

{

//n=2^k(k>=1)个选手参加比赛,二维数组a表示日程安排,数组下标从1开始

int n=2;//k=0,两个选手比赛日程可直接求得

//求解两个选手比赛日程,得到左上角元素

a[1][1]=1;a[1][2]=2;

a[2][1]=2;a[2][2]=1;

int i,j,t;

for(t=1;t<k;t++)//迭代处理,依次处理2^2,....,2^k个选手比赛日程

{

int temp=n;n=n*2;

//填左下角元素

for(i=temp+1;i<=n;i++)

for(j=1;j<=temp;j++)

a[i][j]=a[i-temp][j]+temp;//左下角元素和左上角元素的对应关系

//将左下角元素抄到右上角

for(i=1;i<=temp;i++)

for(j=temp+1;j<=n;j++)

a[i][j]=a[i+temp][(j+temp)%n];

//将左上角元素抄到右下角

for(i=temp+1;i<=n;i++)

for(j=temp+1;j<=n;j++)

a[i][j]=a[i-temp][j-temp];

}

for(i=1;i<=n;i++)//显示日程表

for(j=1;j<=n;j++)

{

printf("- ",a[i][j]);

if(j==n)

printf("n");

}

}

void main()

{

int a[N][N];

int k;

printf("输入选手的个数:(注意为2的平方)");

scanf("%d",&k);

GameTable(k,a);

}

2、结果验证

当两个选手,即k=1时

当4个选手时,即k=2

当8个选手,即k=3

当16个选手时,即k=16

时间复杂度分析:

迭代处理的循环体内部3个循环语句,每个循环语句都是一个嵌套的for循环,且它们的执行次数相同,基本语句是最内层循环体的赋值语句,即填写比赛日程表的元素。基本执行语句的执行次数是:

T(n)=

所以时间复杂度为O(4k)

改进的算法:

#include<iostream>

using namespace std;

const int SIZE = 50;

int a[SIZE][SIZE];

void copy(int n);

void tournament(int n);

int odd(int n);  //判断奇偶性

void makecopy(int n);  //makecopy 与copy算法类似,但是区分了n/2为奇数或偶数的情形

void copyodd(int n);  // 实现n/2为奇数时的复制

void  main()

{

int n;

int i,j;

cin >> n;

tournament(n);

if(odd(n))   // n为奇数和偶数输出情况不同,要分别考虑

{

for(i = 1; i<=n; i++)

{

for(j = 1; j<=n+1; j++)

if(a[i][j] == n+1)

cout << "0  ";

else

cout << a[i][j] << "  " ;

cout << endl;

}

}

else

{

for(i = 1; i<=n; i++)

{

for(j = 1; j<=n; j++)

cout << a[i][j] << "  " ;

cout << endl;

}

}

}

void copy(int n)

{

int m = n/2;

for(int i = 1; i<=m; i++)

for(int j = 1; j<=m; 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];

}

}

void tournament(int n)

{

if(n == 1)

{

a[1][1] = 1;

return;

}

if(odd(n))

{

tournament(n+1);

return;

}

tournament(n/2);

makecopy(n);

}

int  odd(int n)

{

if(n%2==1)

return 1;

else return 0;

}

void makecopy(int n)  //makecopy 与copy算法类似,但是要区分n/2为奇数或偶数的情形

{

if(n/2 > 1 && odd(n/2))

copyodd(n);

else

copy(n);

}

void copyodd(int n)        // 实现n/2为奇数时的复制

{

int b[SIZE];

int m = n/2;

for(int i = 1; i<=m; i++)

{

b[i] = m+i;

b[m+i] = b[i];

}

for(i = 1; i<=m; i++)

{

for(int j=1; j<=m+1; j++)

{

if(a[i][j] > m)

{

a[i][j] = b[i];

a[m+i][j] = (b[i] + m)%n;

}

else

a[m+i][j] = a[i][j] + m;

}

for(j = 2; j<=m; j++)

{

a[i][m+j] = b[i+j-1];

a[b[i+j-1]][m+j] = i;

}

}

}

结果验证:

当参赛人数为偶数 8时

当参赛人数为奇数  7时

时间复杂度:O(4k)

下面是一个N=8的计算过程,可以帮助理解

问题描述

设有n=2^k个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:

        (1)每个选手必须与其他n-1个选手各赛一次;
     (2)每个选手一天只能参赛一次;
     (3)循环赛在n-1天内结束。

请按此要求将比赛日程表设计成有n行和n-1列的一个表。在表中的第i行,第j列处填入第i个选手在第j天所遇到的选手。其中1≤i≤n,1≤j≤n-1。8个选手的比赛日程表如下图:

 算法思路按分治策略,我们可以将所有的选手分为两半,则n个选手的比赛日程表可以通过n/2个选手的比赛日程表来决定。递归地用这种一分为二的策略对选手进行划分,直到只剩下两个选手时,比赛日程表的制定就变得很简单。这时只要让这两个选手进行比赛就可以了。如上图,所列出的正方形表是8个选手的比赛日程表。其中左上角与左下角的两小块分别为选手1至选手4和选手5至选手8前3天的比赛日程。据此,将左上角小块中的所有数字按其相对位置抄到右下角,又将左下角小块中的所有数字按其相对位置抄到右上角,这样我们就分别安排好了选手1至选手4和选手5至选手8在后4天的比赛日程。依此思想容易将这个比赛日程表推广到具有任意多个选手的情形。

算法步骤

(1)用一个for循环输出日程表的第一行 for(int i=1;i<=N;i++) a[1][i] = i

(2)然后定义一个m值,m初始化为1,m用来控制每一次填充表格时i(i表示行)和j(j表示列)的起始填充位置。

(3)用一个for循环将问题分成几部分,对于k=3,n=8,将问题分成3大部分,第一部分为,根据已经填充的第一行,填写第二行,第二部分为,根据已经填充好的第一部分,填写第三四行,第三部分为,根据已经填充好的前四行,填写最后四行。for (ints=1;s<=k;s++)  N/=2;

(4)用一个for循环对③中提到的每一部分进行划分for(intt=1;t<=N;t++)对于第一部分,将其划分为四个小的单元,即对第二行进行如下划分

同理,对第二部分(即三四行),划分为两部分,第三部分同理。


     (5)最后,根据以上for循环对整体的划分和分治法的思想,进行每一个单元格的填充。填充原则是:对角线填充

for(int i=m+1;i<=2*m;i++) //i控制行

for(int j=m+1;j<=2*m;j++)  //j控制列

{

a[i][j+(t-1)*m*2]= a[i-m][j+(t-1)*m*2-m];/*右下角的值等于左上角的值 */

a[i][j+(t-1)*m*2-m] =a[i-m][j+(t-1)*m*2];/*左下角的值等于右上角的值 */


         }  

运行过程

(1)由初始化的第一行填充第二行

(2)由s控制的第一部分填完。然后是s++,进行第二部分的填充

(3)最后是第三部分的填充

程序清单

  1. //2d11 分治法,循环赛事日程表
  2. #include "stdafx.h"
  3. #include <iostream>
  4. #include <math.h>
  5. using namespace std;
  6. void Table(int k,int n,int **a);
  7. void input(int &k);
  8. void output(int **a,int n);
  9. int main()
  10. {
  11. int k;
  12. input(k);
  13. int n=1;
  14. //n=2k(k>=1)个选手参加比赛
  15. for(int i=1; i<=k; i++)
  16. n *= 2;
  17. //根据n动态分配二维数组a
  18. int **a = new int *[n+1];
  19. for(int i=0;i<=n;i++)
  20. {
  21. a[i] = new int[n+1];
  22. }
  23. Table(k,n,a);
  24. cout<<"循环赛事日程表为:"<<endl;
  25. output(a,n);
  26. //释放空间
  27. for(int i=0;i<=n;i++)
  28. {
  29. delete[] a[i];
  30. }
  31. delete[] a;
  32. return 0;
  33. }
  34. void input(int &k)
  35. {
  36. cout<<"请输入k值:"<<endl;
  37. cin>>k;
  38. }
  39. void output(int **a,int n)
  40. {
  41. for(int i=1; i<=n; i++)
  42. {
  43. for(int j=1; j<=n; j++)
  44. {
  45. cout<<a[i][j]<<" ";
  46. }
  47. cout<<endl;
  48. }
  49. }
  50. void Table(int k,int n,int **a)
  51. {
  52. for(int i=1; i<=n; i++)
  53. a[1][i]=i;//设置日程表第一行
  54. int m = 1;//每次填充时,起始填充位置
  55. for(int s=1; s<=k; s++)
  56. {
  57. n /= 2;
  58. for(int t=1; t<=n; t++)
  59. {
  60. for(int i=m+1; i<=2*m; i++)//控制行
  61. {
  62. for(int j=m+1; j<=2*m; j++)//控制列
  63. {
  64. a[i][j+(t-1)*m*2] = a[i-m][j+(t-1)*m*2-m];//右下角等于左上角的值
  65. a[i][j+(t-1)*m*2-m] = a[i-m][j+(t-1)*m*2];//左下角等于右上角的值
  66. }
  67. }
  68. }
  69. m *= 2;
  70. }
  71. }


程序运行结果:

 

循环赛日常表算法(N可为奇数和偶数)的更多相关文章

  1. 正整数构成的线性表存放在单链表中,编写算法将表中的所有的奇数删除。(C语言)

    /* 正整数构成的线性表存放在单链表中,编写算法将表中的所有的奇数删除 */ #include <stdio.h> #include <stdlib.h> typedef st ...

  2. Hash表算法

    出处:http://blog.csdn.net/v_JULY_v 第一部分:Top K 算法详解问题描述百度面试题:    搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的 ...

  3. 十一、从头到尾彻底解析Hash 表算法

    在研究MonetDB时深入的学习了hash算法,看了作者的文章很有感触,所以转发,希望能够使更多人受益! 十一.从头到尾彻底解析Hash 表算法 作者:July.wuliming.pkuoliver  ...

  4. 从头到尾彻底解析Hash表算法

    作者:July.wuliming.pkuoliver 说明:本文分为三部分内容, 第一部分为一道百度面试题Top K算法的详解:第二部分为关于Hash表算法的详细阐述:第三部分为打造一个最快的Hash ...

  5. 从头到尾解析Hash表算法

    via:点击打开链接 十一.从头到尾解析Hash 表算法 作者:July.wuliming.pkuoliver   出处:http://blog.csdn.net/v_JULY_v.   说明:本文分 ...

  6. 从头到尾彻底解析Hash 表算法

    作者:July.wuliming.pkuoliver  出处:http://blog.csdn.net/v_JULY_v.  说明:本文分为三部分内容,    第一部分为一道百度面试题Top K算法的 ...

  7. SQL-61 对于employees表中,给出奇数行的first_name

    题目描述 对于employees表中,给出奇数行的first_nameCREATE TABLE `employees` (`emp_no` int(11) NOT NULL,`birth_date` ...

  8. (面试)Hash表算法十道海量数据处理面试题

    Hash表算法处理海量数据处理面试题 主要针对遇到的海量数据处理问题进行分析,参考互联网上的面试题及相关处理方法,归纳为三种问题 (1)数据量大,内存小情况处理方式(分而治之+Hash映射) (2)判 ...

  9. Hash表算法详解

    Hash表定义 散列表(Hash table,也叫哈希表),是根据关键字值(Key value)直接进行访问的数据结构.也就是说,它通过把关键字(关键字通过Hash算法生成)映射到表中一个位置来访问记 ...

随机推荐

  1. ggplot饼图

    目录: 原始图样 如何去除饼图中心的杂点 如何去除饼图旁边的标签 如何去掉左上角多出来的一横线 如何去掉图例的标题,并将图例放到上面 如何对图例的标签加上百分比 如何让饼图的小块按顺时针从大到小的顺序 ...

  2. 【转】优秀PMP项目经理必备的8个要素

    结合本人这几年在项目管理上的总结和得失,本人认为优秀的项目经理要有 责任心.要 善于沟通.能 引导客户.能 预测风险. 善于总结. 随需应变.善于 激励团队.同时也要 懂技术. 责任心 作为项目经理首 ...

  3. Step download timeout (120 sec)

    Step download timeout (120 sec)  --------- Troubleshooting-----------------------------------    修改S ...

  4. jQuery方法笔记

    .clone() $(selector).clone(includeEvents) $(this).clone(true) //boolean值,true/false分别对饮是否复制元素的所有事件处理

  5. UML---UML中的几种关系(依赖,关联,泛化,实现)

    关于设计模式的总结没有落实到代码上,而且设计模式自己确实动手实现的非常少.所以在这一周里,除了看网站开发的视频,着手开始对设计模式进行实现以下.设计模式非常经典,每次看都有不同的收获,写一下自己的收获 ...

  6. 前台的js对象数组传到后台处理。在前台把js对象数组转化为json字符串,在后台把json字符串解析为List<>

    前台的js对象数组传到后台处理.在前台把js对象数组转化为json字符串,在后台把json字符串解析为List<>

  7. 手机web不同屏幕字体大小高度自适应

    body{font-size:0.6rem:} <script> //使用rem策略,不断更新html的fontsize (function(){     function sizeHtm ...

  8. csu1510 Happy Robot 递推

    题目链接: cid=2095&pid=7">csu1510 解题思路: 要求解四个值x_min,x_max,y_min,y_max 首先考虑x_min怎样得到:由于机器人最后有 ...

  9. Kafka中Producer端封装自定义消息

    我们知道KeywordMessage就是被kafka发送和存储的对象.所以只需要模拟出这个就可以发送自定义消息了. 比如我需要将用户的id,user,age,address和访问ip和访问date记录 ...

  10. IT教程视频

    声明:以下视频均来自与互联网各个高级培训机构内部视频,我们能保证大部分的链接均可用.但不能保证所有的视频内容都是最新的.如果想要实时跟进各个培训机构的内部视频建议您关注微信公众号(BjieCoder) ...