在一个知名企业赞助的足球联赛中,有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 public static (int, int)[][] BuildMatchesTable(int n)
2 {
3 var matches = new (int, int)[n - 1][]; // 创建一个二维数组,表示比赛表
4 var teams = FairCycle(n).GetEnumerator(); // 获取一个循环迭代器,用于生成比赛对阵
5 for (int i = 0; i < n - 1; i++)
6 {
7 var round = new (int, int)[n / 2]; // 创建一个一维数组,表示当前轮次的比赛对阵
8 for (int t = 0; t < n / 2; t++)
9 {
10 teams.MoveNext(); // 迭代到下一个比赛对阵
11 round[t] = teams.Current; // 将当前比赛对阵添加到当前轮次的比赛表中
12 }
13 matches[i] = round; // 将当前轮次的比赛表添加到总的比赛表中
14 // FairCycle函数会在这里负责循环到下一个比赛对阵
15 }
16 return matches; // 返回生成的比赛表
17 }
18
19 static IEnumerable<(int,int)> FairCycle(int n)
20 {
21 // 将数字1到n-1按顺时针方式排列
22 // 从12点钟方向开始,1位于12点钟位置
23 int p = 1;
24 while (true)
25 {
26 // 返回n和12点钟位置的数字
27 yield return (p, n);
28 // 然后返回12点钟左右两侧的数字配对
29 // 例如,11和1点钟,10和2点钟,以此类推
30 for (int i = 1; i < n / 2; i++)
31 {
32 int l = p + i;
33 l = l > (n - 1) ? l - (n - 1) : l; // 对n进行循环,但不包括n本身
34 int r = p + (n - 1) - i;
35 r = r > (n - 1) ? r - (n - 1) : r;
36 yield return (l, r);
37 }
38 // 将时钟旋转n/2,使得p + n/2现在位于12点钟位置
39 p += n / 2;
40 p = p > (n - 1) ? p - (n - 1) : p;
41 // 这将开始下一轮的配对
42 }
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 using NUnit.Framework;
2 using System;
3 using System.Linq;
4 using System.Collections.Generic;
5
6 namespace Solution {
7
8 [TestFixture]
9 public class SolutionTest
10 {
11 [Test]
12 public void Test2Teams()
13 {
14 var expected = new []{ new []{(1, 2)} };
15
16 var actual = Tournament.BuildMatchesTable(2);
17 Assert.That(actual, Has.Length.EqualTo(1), "Should have 1 round");
18 Assert.That(actual[0], Has.Length.EqualTo(1), "The round should have 1 match");
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 Assert.AreEqual(expected, actual, "The match should be team 1 vs team 2");
21 }
22
23 [Test]
24 public void Test4Teams() => TestTeams(4);
25
26 [Test]
27 public void Test20Teams() => TestTeams(20);
28
29 [Test]
30 public void TestRandom()
31 {
32 Random rand = new Random();
33
34 TestTeams(2*(3+rand.Next(3)));
35 TestTeams(2*(6+rand.Next(4)));
36 }
37
38 public void TestTeams(int numberOfTeams)
39 {
40 List<int> teamsExpected = Enumerable.Range(1, numberOfTeams).ToList();
41 HashSet<(int, int)> matchesExpected = new HashSet<(int, int)>();
42 foreach(var round in TournamentSolution.BuildMatchesTable(numberOfTeams))
43 {
44 foreach(var game in round) matchesExpected.Add(game.Item1>game.Item2?(game.Item2,game.Item1):(game.Item1, game.Item2));
45 }
46
47 var actual = Tournament.BuildMatchesTable(numberOfTeams);
48 Assert.That(actual, Has.Length.EqualTo(numberOfTeams-1), $"Should have {numberOfTeams-1} rounds");
49 foreach(var round in actual)
50 {
51 List<int> teamsByRound = new List<int>();
52 Assert.That(round, Has.Length.EqualTo(numberOfTeams/2), $"Each round should have {numberOfTeams/2} matches");
53 foreach(var game in round)
54 {
55 Assert.That(game, Is.InstanceOf(typeof((int, int))), "Each match is a tupple of 2 teams");
56 teamsByRound.Add(game.Item1);
57 teamsByRound.Add(game.Item2);
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 }
60 teamsByRound.Sort();
61 Assert.AreEqual(teamsExpected, teamsByRound, "Each round should have matches with every team");
62 }
63 Assert.IsEmpty(matchesExpected, "At least one match isn't scheduled");
64 }
65 }
66 }
67
68 public class TournamentSolution
69 {
70 public static (int, int)[][] BuildMatchesTable(int numberOfTeams)
71 {
72 List<int> teams = Enumerable.Range(1, numberOfTeams).ToList();
73 int roundsNbr = numberOfTeams-1, gamesNbr = numberOfTeams /2, rotatorID = roundsNbr-1, buffer = 0;
74 (int, int)[][] result = new (int, int)[roundsNbr][];
75
76 for (int i=0; i<roundsNbr; i++)
77 {
78 result[i] = new (int, int)[gamesNbr];
79
80 for (int j = 0; j < gamesNbr; j++) result[i][j] = (teams[0 + j], teams[roundsNbr - j]);
81
82 buffer = teams[rotatorID];
83 teams.RemoveAt(rotatorID);
84 teams.Insert(0, buffer);
85 }
86
87 return result;
88 }
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. ENVI5.3 安装教程,新手入门(超详细)附安装包和常见问题

    ENVI是一个完整的遥感图像处理平台,广泛应用于科研.环境保护.气象.农业.林业.地球科学.遥感工程.水利.海洋等领域.目前ENVI已成为遥感影像处理的必备软件,包含辐射定标.大气校正.镶嵌裁剪.分类 ...

  2. 如何解决PyCharm中运行不了python代码的问题

    一.问题分析 一般是新手小白才会出现这个问题.刚入门python或者Web自动化测试的集美们很多都会选择使用PyCharm来运行python,但是下载安装完PyCharm后,新建了一个python项目 ...

  3. NVIDIA Maxine Video Effects SDK 編程指南 - 实践小记

    NVIDIA Maxine Video Effects SDK 編程指南 - 实践小记 本篇博客重点只说Video Effect的部分,此外还有Audio Effect的部分.还有AR部分,不在本篇范 ...

  4. java接口返回图片链接或pdf链接如何设置在线预览还是下载

    之前文章说到了如何通过将文件转成图片或者pdf来实现在线预览,一般来说在线预览图片或者pdf都是存储在图片服务器上的,在通过接口调用把文件返回给前端,但是把文件返回给前端效果一般是有两种:在线预览和下 ...

  5. 全同态(Fully Homomorphic Encryption, FHE)和半同态(Partially Homomorphic Encryption, PHE)介绍

    全同态(Fully Homomorphic Encryption, FHE)和半同态(Partially Homomorphic Encryption, PHE) 全同态加密(FHE)是指一种加密方案 ...

  6. Java 访问控制权限修饰符

    1.访问控制权限修饰符来控制元素的访问范围 2.访问控制权限修饰符包括: public 表示公开的,任何位置都可以可以访问 protected 同包,子类 缺省 同包 private 表示私有的,只能 ...

  7. Taurus .Net Core 微服务开源框架:Admin 插件【3】 - 指标统计管理

    前言: 继上篇:Taurus .Net Core 微服务开源框架:Admin 插件[2] - 系统环境信息管理 本篇继续介绍下一个内容: 1.系统指标节点:Metric - API 界面 界面图如下: ...

  8. 使用 JCommander 解析命令行参数

    前言 如果你想构建一个支持命令行参数的程序,那么 jcommander 非常适合你,jcommander 是一个只有几十 kb 的 Java 命令行参数解析工具,可以通过注解的方式快速实现命令行参数解 ...

  9. HCL实验6:静态路由

    拓扑图 步骤: 连线,路由器与路由器通过S端口连接 配置好PC 配置路由器端口IP 配置路由器的下一跳地址(静态路由) 详细步骤 连线情况可见拓扑图 配置好PC 端口IP R1 [R1]int g0/ ...

  10. 【调制解调】VSB 残留边带调幅

    说明 学习数字信号处理算法时整理的学习笔记.同系列文章目录可见 <DSP 学习之路>目录,代码已上传到 Github - ModulationAndDemodulation.本篇介绍 VS ...