在一个知名企业赞助的足球联赛中,有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. SCI 投稿中像素、DPI、图片分辨率的一些知识

    最近在学习 Linux 命令行下的 ImageMagick 图像处理,对图像本身的一些概念有点懵,搜集整理了一点资料,仅供自己和大家学习与参考. SCI 期刊对分辨率大多都有一定的要求,例如一段来自 ...

  2. 在R中子集化数据框的5种方法

    由于微信不允许外部链接,你需要点击文章尾部左下角的 "阅读原文",才能访问文中链接. 通常,我们在使用大型数据集时,只会对其中的一小部分感兴趣,用以进行特定分析. 那么,我们应该如 ...

  3. CSI架构和原理

    CSI CSI简介 CSI的诞生背景 K8s 原生支持一些存储类型的 PV,如 iSCSI.NFS.CephFS 等等,这些 in-tree 类型的存储代码放在 Kubernetes 代码仓库中.这里 ...

  4. JMH – Java基准测试

    官方资源 官方Github样例 应用场景 对要使用的数据结构不确定,不知道谁的性能更好 对历史方法代码重构,要评判改造之后的性能提升多少 (我要做的场景) 想准确地知道某个方法需要执行多长时间,以及执 ...

  5. Python Django 零基础从零到一部署服务,Hello Django!全文件夹目录和核心代码!

    在这篇文章中,我将手把手地教你如何从零开始部署一个使用Django框架的Python服务.无论你是一个刚开始接触开发的新手,还是一个有经验的开发者想要快速了解Django,这篇教程都会为你提供一条清晰 ...

  6. 零基础如何自学C#?

    前言 本文来源于知乎的一个提问,提问的是一个大一软件工程专业的学生,他想要自学C#但是不知道该怎么去学,这让他感到很迷茫,希望有人能给他一些建议和提供一些学习方向. 个人建议 确认目标:自学C#首先你 ...

  7. CF371D Vessels题解

    思路: 定义一个权值并查集,权值保存这个集合还可以存下多少水. 如果这个集合可以存放的水已经小于要装入的水,就将这个集合与下一个集合合并. 否则,直接把这个集合可以存放的水减去要装入的水的体积. 代码 ...

  8. (内附示例源码)如何通过electron构建桌面跨平台音视频应用

    近年来,视频直播.直播带货.在线教育.在线医疗等音视频领域的相关行业都非常热门,成为大众瞩目的焦点. 在不久的将来,音视频技术渗透于各行各业,无处不在.从IoT网络到个人用户的移动设备,音视频技术以不 ...

  9. Spring—bean的作用域

    beans的作用域 单例模式(Spring默认模式) <?xml version="1.0" encoding="UTF-8"?> <bean ...

  10. Git练习网址

    爲了方便学习git指令,让新手们更容易地理解,所以推荐一些git练习和博文网址 推荐的网址如下 网址一:Learn Git Branching! https://learngitbranching.j ...