在一个知名企业赞助的足球联赛中,有256支球队参赛。为了确保比赛的顺利进行,企业指派了小悦负责熬夜加班制定每一个球队的赛程。尽管她对足球的了解并不多,但是她对待工作的认真态度却让人钦佩。

在小悦的努力下,她顺利完成了第一轮、第二轮和第三轮的比赛安排。然而,在大赛开始前的模拟比赛中,她发现了一个严重的问题:由于参赛球队过多,人为的安排总会导致一些参赛球队被遗漏了比赛。这让她十分焦虑,因为如果不能尽快解决这个问题,联赛的公平性和竞争性将受到严重影响。

为了解决这个问题,小悦开始了她的电话咨询之旅。她先是联系了赛事主办方,了解参赛球队的具体情况。随后,她又联系了计算机专家,希望找到一个解决办法,确保每个参赛球队都能顺利比赛。

在与计算机专家沟通的过程中,小悦了解到这个问题并不简单,因为要考虑到的因素非常多。不过,在专家的帮助下,她逐渐找到了问题的根源,并听取专家的意见采取了一些有效的措施来解决它。最终,经过小悦的不懈努力和计算机专家的协助,每个参赛球队的赛程都被安排得合理而公正。

小悦在熬夜加班制定赛程时,扎着马尾辫,聚精会神地盯着电脑屏幕,手指在键盘上飞快地敲击着。她的眼神中闪烁着智慧的光芒,仿佛在告诉人们:她有能力解决任何问题。当她下意识挽起头发时,这个简单的动作,展现了她的优雅和美丽。

在这个过程中,小悦学到了很多关于足球和赛程安排的知识。她也深刻体会到团队合作的重要性,学会了如何与同事合作解决问题。最终,她的努力得到了上司和公司的肯定,也为她带来了不少宝贵的经验。

小悦面临的问题如下:锦标赛由256支队伍组成。这是一场循环赛(全部比赛),所以有255轮,每支球队每轮比赛一次。每支球队在锦标赛中与其他球队对抗一次(每场比赛在锦标赛中不会重复)。

她的任务是实现一个函数buildMatchesTable,它接收团队的数量(总是正数和偶数)并返回一个矩阵。

矩阵中的每一行代表一轮。矩阵的每一列表示一个匹配。这场比赛是由两支队伍组成的一个阵列。每个团队都用一个数字表示,从1开始直到团队数量。

示例(假设4只球队参赛): 构建匹配表(4)

应该返回一个元组矩阵,如下所示:

{

new[]{(1,2),(3,4)},//第一轮:1对2,3对4

new[]{(1,3),(2,4)},//第二轮:1对3,2对4

new[]{(1,4),(2,3)}//第三轮:1对4,2对3

}


算法实现:

  1. 1 public static (int, int)[][] BuildMatchesTable(int n)
  2. 2 {
  3. 3 var matches = new (int, int)[n - 1][]; // 创建一个二维数组,表示比赛表
  4. 4 var teams = FairCycle(n).GetEnumerator(); // 获取一个循环迭代器,用于生成比赛对阵
  5. 5 for (int i = 0; i < n - 1; i++)
  6. 6 {
  7. 7 var round = new (int, int)[n / 2]; // 创建一个一维数组,表示当前轮次的比赛对阵
  8. 8 for (int t = 0; t < n / 2; t++)
  9. 9 {
  10. 10 teams.MoveNext(); // 迭代到下一个比赛对阵
  11. 11 round[t] = teams.Current; // 将当前比赛对阵添加到当前轮次的比赛表中
  12. 12 }
  13. 13 matches[i] = round; // 将当前轮次的比赛表添加到总的比赛表中
  14. 14 // FairCycle函数会在这里负责循环到下一个比赛对阵
  15. 15 }
  16. 16 return matches; // 返回生成的比赛表
  17. 17 }
  18. 18
  19. 19 static IEnumerable<(int,int)> FairCycle(int n)
  20. 20 {
  21. 21 // 将数字1到n-1按顺时针方式排列
  22. 22 // 从12点钟方向开始,1位于12点钟位置
  23. 23 int p = 1;
  24. 24 while (true)
  25. 25 {
  26. 26 // 返回n和12点钟位置的数字
  27. 27 yield return (p, n);
  28. 28 // 然后返回12点钟左右两侧的数字配对
  29. 29 // 例如,11和1点钟,10和2点钟,以此类推
  30. 30 for (int i = 1; i < n / 2; i++)
  31. 31 {
  32. 32 int l = p + i;
  33. 33 l = l > (n - 1) ? l - (n - 1) : l; // 对n进行循环,但不包括n本身
  34. 34 int r = p + (n - 1) - i;
  35. 35 r = r > (n - 1) ? r - (n - 1) : r;
  36. 36 yield return (l, r);
  37. 37 }
  38. 38 // 将时钟旋转n/2,使得p + n/2现在位于12点钟位置
  39. 39 p += n / 2;
  40. 40 p = p > (n - 1) ? p - (n - 1) : p;
  41. 41 // 这将开始下一轮的配对
  42. 42 }
  43. 43 }

这段代码实现了一个生成比赛表的函数`BuildMatchesTable`和一个辅助函数`FairCycle`。

`BuildMatchesTable`函数接收一个正数n作为参数,返回一个二维数组,表示比赛表。

`FairCycle`函数是一个模拟时钟循环迭代器,用于生成球队比赛对阵表。

让我们以`BuildMatchesTable(4)`为例来详细介绍`yield return`的运行过程和直接使用`return`的运行过程:

首先,我们调用`BuildMatchesTable(4)`函数,它将生成一个4个参与者的比赛表。

执行`var teams = FairCycle(n).GetEnumerator();`时会调用`FairCycle(n)`方法,并开始执行其中的代码。在`FairCycle(n)`方法中,第一次调用`yield return`语句会返回一个`IEnumerator`对象,该对象用于迭代生成比赛对阵。

这意味着在执行`var teams = FairCycle(n).GetEnumerator();`之后,`teams`变量将持有一个可以用于迭代生成比赛对阵的迭代器对象。你可以使用`teams.MoveNext()`方法来逐个获取比赛对阵,每次调用`MoveNext()`方法时,都会执行`FairCycle(n)`方法中的代码,直到遇到下一个`yield return`语句。

使用`yield return`的运行过程如下:

1. 初始化时钟位置p为1。
2. 进入无限循环。
3. 第一次调用`yield return`语句,返回当前时钟位置p和数字n的配对`(p, n)`,即`(1, 4)`。此时,比赛表中的第一场比赛是1号选手对阵4号选手。
4. 继续循环,进入第二次调用`yield return`语句。
- 计算左侧数字l,它等于p加上i,即2。
- 计算右侧数字r,它等于p加上(n-1)-i,即3。
- 使用`yield return`语句返回左侧数字l和右侧数字r的配对`(l, r)`,即`(2, 3)`。此时,比赛表中的第二场比赛是2号选手对阵3号选手。
5. 更新时钟位置p,使其加上n/2,即2。此时,时钟位置p指向下一轮的起始位置。
6. 回到步骤3,开始下一轮的配对。
- 第三次调用`yield return`语句,返回当前时钟位置p和数字n的配对`(p, n)`,即`(2, 4)`。此时,比赛表中的第三场比赛是2号选手对阵4号选手。
- 继续循环,进入第四次调用`yield return`语句。
- 计算左侧数字l,它等于p加上i,即3。
- 计算右侧数字r,它等于p加上(n-1)-i,即2。
- 使用`yield return`语句返回左侧数字l和右侧数字r的配对`(l, r)`,即`(3, 2)`。此时,比赛表中的第四场比赛是3号选手对阵2号选手。
- 更新时钟位置p,使其加上n/2,即4。此时,时钟位置p指向下一轮的起始位置。
- 回到步骤3,开始下一轮的配对。由于此时时钟位置p超过了(n-1),即4,所以将其减去(n-1),即得到1。此时,时钟位置p重新指向下一轮的起始位置。
- 第五次调用`yield return`语句,返回当前时钟位置p和数字n的配对`(p, n)`,即`(1, 4)`。此时,比赛表中的第五场比赛是1号选手对阵4号选手。
- 继续循环,进入第六次调用`yield return`语句。
- 计算左侧数字l,它等于p加上i,即2。
- 计算右侧数字r,它等于p加上(n-1)-i,即3。
- 使用`yield return`语句返回左侧数字l和右侧数字r的配对`(l, r)`,即`(2, 3)`。此时,比赛表中的第六场比赛是2号选手对阵3号选手。
- 更新时钟位置p,使其加上n/2,即2。此时,时钟位置p指向下一轮的起始位置。
- 回到步骤3,开始下一轮的配对。由于此时时钟位置p超过了(n-1),即4,所以将其减去(n-1),即得到1。此时,时钟位置p重新指向下一轮的起始位置。
- ...

这样,`yield return`语句会按需生成比赛对阵,每次调用时返回一个比赛对阵,并在下一次调用时从迭代器Enumerator上一次离开的地方继续执行。

现在,让我们来看看直接使用`return`的运行过程:

1. 初始化时钟位置p为1。
2. 直接使用`return`语句返回当前时钟位置p和数字n的配对`(p, n)`,即`(1, 4)`。此时,方法立即终止执行,并将配对`(1, 4)`作为方法的结果返回。这意味着只会生成一个比赛对阵,即1号选手对阵4号选手。

使用`return`语句会立即终止方法的执行,并将指定的值作为方法的结果返回。这意味着方法只会生成一个比赛对阵,并且无法再继续执行其他的代码。


注:FairCycle方法设计为时钟的模式是为了确保每个参赛球队都能在锦标赛中与其他球队公平地对抗一次。该方法的好处包括:

  1. 公平性:通过时钟的模式(所有球队对半匹配一次),每支球队都有机会与其他球队进行比赛,确保了比赛的公平性。没有球队会被遗漏或被偏好,每个球队都有相同的机会竞争。

  2. 竞争性:FairCycle方法确保了每支球队都能面对各种对手,包括实力强大的球队和实力较弱的球队。这种多样性的对手使得比赛更具竞争性,增加了球队之间的激烈程度。

  3. 简洁性:时钟的模式使得比赛安排更加简洁明了。每支球队在每轮比赛中都有一个确定的对手,没有重复或遗漏的情况,减少了混乱和错误的可能性。

  4. 可预测性:由于时钟的模式,每支球队都可以提前知道在每一轮比赛中将要面对的对手。这样,球队可以提前制定战术和策略,更好地准备比赛。

假设有8支球队参加一个锦标赛,并且FairCycle方法被用来安排比赛。FairCycle方法的时钟模式将确保每支球队都能与其他球队公平地对抗一次。

首先,我们将8支球队编号为A、B、C、D、E、F、G、H。根据FairCycle方法,比赛的安排如下:

第一轮: A vs B C vs D E vs F G vs H

第二轮: A vs C B vs D E vs G F vs H

第三轮: A vs D B vs C E vs H F vs G

第四轮: A vs E B vs F C vs G D vs H

第五轮: A vs F B vs E C vs H D vs G

第六轮: A vs G B vs H C vs E D vs F

第七轮: A vs H B vs G C vs F D vs E

通过这个例子,我们可以看到每支球队都与其他球队公平地对抗了一次。FairCycle方法的时钟模式确保了每支球队都有相同的机会竞争,没有球队被遗漏或被偏好。这种安排方式确保了比赛的公平性,并且每支球队都有机会面对各种对手,增加了比赛的竞争性。


测试用例:

  1. 1 using NUnit.Framework;
  2. 2 using System;
  3. 3 using System.Linq;
  4. 4 using System.Collections.Generic;
  5. 5
  6. 6 namespace Solution {
  7. 7
  8. 8 [TestFixture]
  9. 9 public class SolutionTest
  10. 10 {
  11. 11 [Test]
  12. 12 public void Test2Teams()
  13. 13 {
  14. 14 var expected = new []{ new []{(1, 2)} };
  15. 15
  16. 16 var actual = Tournament.BuildMatchesTable(2);
  17. 17 Assert.That(actual, Has.Length.EqualTo(1), "Should have 1 round");
  18. 18 Assert.That(actual[0], Has.Length.EqualTo(1), "The round should have 1 match");
  19. 19 if(actual[0][0].Item1>actual[0][0].Item2) (actual[0][0].Item1, actual[0][0].Item2) = (actual[0][0].Item2, actual[0][0].Item1);
  20. 20 Assert.AreEqual(expected, actual, "The match should be team 1 vs team 2");
  21. 21 }
  22. 22
  23. 23 [Test]
  24. 24 public void Test4Teams() => TestTeams(4);
  25. 25
  26. 26 [Test]
  27. 27 public void Test20Teams() => TestTeams(20);
  28. 28
  29. 29 [Test]
  30. 30 public void TestRandom()
  31. 31 {
  32. 32 Random rand = new Random();
  33. 33
  34. 34 TestTeams(2*(3+rand.Next(3)));
  35. 35 TestTeams(2*(6+rand.Next(4)));
  36. 36 }
  37. 37
  38. 38 public void TestTeams(int numberOfTeams)
  39. 39 {
  40. 40 List<int> teamsExpected = Enumerable.Range(1, numberOfTeams).ToList();
  41. 41 HashSet<(int, int)> matchesExpected = new HashSet<(int, int)>();
  42. 42 foreach(var round in TournamentSolution.BuildMatchesTable(numberOfTeams))
  43. 43 {
  44. 44 foreach(var game in round) matchesExpected.Add(game.Item1>game.Item2?(game.Item2,game.Item1):(game.Item1, game.Item2));
  45. 45 }
  46. 46
  47. 47 var actual = Tournament.BuildMatchesTable(numberOfTeams);
  48. 48 Assert.That(actual, Has.Length.EqualTo(numberOfTeams-1), $"Should have {numberOfTeams-1} rounds");
  49. 49 foreach(var round in actual)
  50. 50 {
  51. 51 List<int> teamsByRound = new List<int>();
  52. 52 Assert.That(round, Has.Length.EqualTo(numberOfTeams/2), $"Each round should have {numberOfTeams/2} matches");
  53. 53 foreach(var game in round)
  54. 54 {
  55. 55 Assert.That(game, Is.InstanceOf(typeof((int, int))), "Each match is a tupple of 2 teams");
  56. 56 teamsByRound.Add(game.Item1);
  57. 57 teamsByRound.Add(game.Item2);
  58. 58 Assert.True(matchesExpected.Remove(game.Item1>game.Item2?(game.Item2,game.Item1):(game.Item1, game.Item2)), $"{game} is a duplicate or doesn't exist");
  59. 59 }
  60. 60 teamsByRound.Sort();
  61. 61 Assert.AreEqual(teamsExpected, teamsByRound, "Each round should have matches with every team");
  62. 62 }
  63. 63 Assert.IsEmpty(matchesExpected, "At least one match isn't scheduled");
  64. 64 }
  65. 65 }
  66. 66 }
  67. 67
  68. 68 public class TournamentSolution
  69. 69 {
  70. 70 public static (int, int)[][] BuildMatchesTable(int numberOfTeams)
  71. 71 {
  72. 72 List<int> teams = Enumerable.Range(1, numberOfTeams).ToList();
  73. 73 int roundsNbr = numberOfTeams-1, gamesNbr = numberOfTeams /2, rotatorID = roundsNbr-1, buffer = 0;
  74. 74 (int, int)[][] result = new (int, int)[roundsNbr][];
  75. 75
  76. 76 for (int i=0; i<roundsNbr; i++)
  77. 77 {
  78. 78 result[i] = new (int, int)[gamesNbr];
  79. 79
  80. 80 for (int j = 0; j < gamesNbr; j++) result[i][j] = (teams[0 + j], teams[roundsNbr - j]);
  81. 81
  82. 82 buffer = teams[rotatorID];
  83. 83 teams.RemoveAt(rotatorID);
  84. 84 teams.Insert(0, buffer);
  85. 85 }
  86. 86
  87. 87 return result;
  88. 88 }
  89. 89 }

【解惑】孜孜不倦,用足球赛程详解c#中的yield return用法的更多相关文章

  1. 详解 javascript中offsetleft属性的用法(转)

    详解 javascript中offsetleft属性的用法 转载  2015-11-11   投稿:mrr    我要评论 本章节通过代码实例介绍一下offsetleft属性的用法,需要的朋友可以做一 ...

  2. 详解AngularJS中的filter过滤器用法

    系统的学习了一下angularjs,发现angularjs的有些思想根php的模块smarty很像,例如数据绑定,filter.如果对smarty比较熟悉的话,学习angularjs会比较容易一点.这 ...

  3. 详解MySQL中concat函数的用法(连接字符串)

    MySQL中concat函数 使用方法: CONCAT(str1,str2,…) 返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. 注意: 如果所有参数均为非二进制 ...

  4. 详解Vue中watch的高级用法

    我们通过实例代码给大家分享了Vue中watch的高级用法,对此知识点有需要的朋友可以跟着学习下. 假设有如下代码: <div> <p>FullName: {{fullName} ...

  5. jQuery:详解jQuery中的事件(二)

    上一篇讲到jQuery中的事件,深入学习了加载DOM和事件绑定的相关知识,这篇主要深入讨论jQuery事件中的合成事件.事件冒泡和事件移除等内容. 接上篇jQuery:详解jQuery中的事件(一) ...

  6. 图文详解Unity3D中Material的Tiling和Offset是怎么回事

    图文详解Unity3D中Material的Tiling和Offset是怎么回事 Tiling和Offset概述 Tiling表示UV坐标的缩放倍数,Offset表示UV坐标的起始位置. 这样说当然是隔 ...

  7. 【转】详解C#中的反射

    原帖链接点这里:详解C#中的反射   反射(Reflection) 2008年01月02日 星期三 11:21 两个现实中的例子: 1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内 ...

  8. 详解Webwork中Action 调用的方法

    详解Webwork中Action 调用的方法 从三方面介绍webwork action调用相关知识: 1.Webwork 获取和包装 web 参数 2.这部分框架类关系 3.DefaultAction ...

  9. 【转】详解JavaScript中的this

    ref:http://blog.jobbole.com/39305/ 来源:foocoder 详解JavaScript中的this JavaScript中的this总是让人迷惑,应该是js众所周知的坑 ...

  10. 深入详解SQL中的Null

    深入详解SQL中的Null NULL 在计算机和编程世界中表示的是未知,不确定.虽然中文翻译为 “空”, 但此空(null)非彼空(empty). Null表示的是一种未知状态,未来状态,比如小明兜里 ...

随机推荐

  1. 高级程序员和新手小白程序员区别你是那个等级看解决bug速度

    IT入门深似海 ,程序员行业,我觉得是最难做的.加不完的班,熬不完的夜. 和产品经理,扯不清,理还乱的宿命关系 一直都在 新需求-做项目-解决问题-解决bug-新需求 好像一直都是这么一个循环.(哈哈 ...

  2. RabbitMQ快速使用代码手册

    本篇博客的内容为RabbitMQ在开发过程中的快速上手使用,侧重于代码部分,几乎没有相关概念的介绍,相关概念请参考以下csdn博客,两篇都是我找的精华帖,供大家学习.本篇博客也持续更新~~~ 内容代码 ...

  3. 【Java学习】 Spring的基础理解 IOC、AOP以及事务

    一.简介     官网: https://spring.io/projects/spring-framework#overview     官方下载工具: https://repo.spring.io ...

  4. 现代C++学习指南-标准库

    在[上一章](https://www.yuque.com/docs/share/adb5b1e4-f3c6-46fd-ba4b-4dabce9b4f2a?# <现代C++学习指南-类型系统> ...

  5. 逍遥自在学C语言 | 多级指针探秘

    前言 多级指针在C语言中是一种特殊的指针类型,它可以指向其他指针的指针. 通过多级指针,我们可以间接地访问或修改存储在内存中的数据. 在本文中,我们将讨论多级指针的概念.使用方法.使用场景以及常见错误 ...

  6. Python运维开发之路《数据类型》

    一. python数据类型 python的五大基本数据类型,数字.字符串.列表.元组.字典;其他数据类型,类型type.Null.文件.集合.函数/方法.类.模块. 1.数字 1 ①整型 2 十进制转 ...

  7. 使用numpy实现bert模型,使用hugging face 或pytorch训练模型,保存参数为numpy格式,然后使用numpy加载模型推理,可在树莓派上运行

    之前分别用numpy实现了mlp,cnn,lstm,这次搞一个大一点的模型bert,纯numpy实现,最重要的是可在树莓派上或其他不能安装pytorch的板子上运行,推理数据 本次模型是随便在hugg ...

  8. 用 Golang 从0到1实现一个高性能的 Worker Pool(一) - 每天5分钟玩转 GPT 编程系列(3)

    目录 1. 概述 2. 设计 2.1 让 GPT-4 给出功能点 2.2 自己总结需求,再给 GPT 派活 3. 实现 3.1 你先随意发挥 3.2 你得让 Worker 跑起来呀 3.3 你说说 P ...

  9. MASABlazor在移动端点击保持出现悬停样式

    提出问题 最近在学习MAUIBlazor,用的MASA Blazor,发现在移动端(触屏设备)上,点击会一直显示悬停样式,如下图所示,简单研究了一下,发现这是移动端的通病. 解决问题 MASABlaz ...

  10. 万字长文浅析配置对MySQL服务器的影响

    有很多的服务器选项会影响这MySQL服务器的性能,比如内存中临时表的大小.排序缓冲区等.有些针对特定存储引擎(如InnoDB)的选项,也会对查询优化很有用. 调整服务器的配置从某种程度来说是一个影响全 ...