快速求出淘汰赛中轮空场次-最简单的算法

 

如果快速求出N个人参加的淘汰赛中轮空的场次数。

N可以使任意数,也就是任意人数参加的比赛。

 

例如:37个人参加淘汰赛,那么无论怎么安排比赛顺序,总是有4场比赛有运动员会轮空。

 

 

答案是:假如m个人参加比赛,N是大于或者等于m的最小2次幂。例如对于m=37来说,N就是2^6=64。那么,令s=N-m。对于m=37来说s=27。那么轮空数Result等于s的二进制表示方法中为1的位的个数!

 

例如对于s=27来说,二进制为11011,那么就一定是4次轮空。而且轮空的场次也跟1出现的次数一致,也就是说,第1,2,4,5场有人轮空比赛。

 

总结,只需要做减法就能规避掉复杂的计算,真是巧夺天工。

 

由此可以推算出世界杯足球赛参赛队伍永远是2的幂,32或者64,不然肯定会有球队少打比赛,造成不公。中国只能期望共有128支球队参加比赛的日子了…………

 

证明方法

关键在于二进制减法(特指N-m)的结果恰好与计算轮空场次的方法的结果一致。为什么我就不知道了。

例如m=37的例子。轮空的计算方法是

第一轮 37/2=18余1

第二轮共18+1=19支队伍比赛 19/2=9余1

第三轮共9+1=10支队伍比赛 10/2=5余0(无轮空)

第四轮共5支队伍比赛 5/2=2余1

第五轮共3支队伍比赛 3/2=1余1

第六轮 自然是决赛 不轮空

所以序列应该是(11011)正好等于64-37=27的二进制形式(11011)。

 

下面来证明“正好”其实是“必然”的:

注意到计算m(37)的二进制形式与计算轮空序列(上述的11011)的关系:

 

计算m的二进制方法:

37/2=18余1

18/2=9余0

9/2=4余1

4/2=2余0

2/2=1余0

1/2=0余1

所以m=37的二进制形式为100101

与上面计算轮空的算法结果的比较是:

1          1

0          1

1          0

0          1

0          1

1

可以看出除开第一个都是为1以外其余都是互补的。这就正好形成了加法进位,最终两个二进制数加一起就是N!而且他们的互补关系是从第一个1出现的时候开始的,也就是产生进位的第一个标志开始。

为什么会这样呢?

其实是因为当计算m的二进制时,我们只是取商,而计算轮空数的时候取商+余数。所以计算二进制的除数总是比计算轮空数的除数小1,造成最后的二进制结果除开第一位以为都是互补的情况(第一位必然是一样的)。

 

证明从第一个1以后互补关系保持的方法:

令从第二轮起m的二进制除数为N,那么轮空数除数为N+1

1.当N为偶数的时候:

第三轮除数为

N/2=N1

N+1/2=N1余1=N1+1

可见,当N为偶数的时候,差1的关系是能维持到下一轮计算的。

2.当N为奇数时:

N/2=N-1/2余1 下一轮除数取N-1/2=N2

N+1/2=N-1/2+1 下一轮除数取N2+1

所以当N为奇数的时候关系也是维持的。

 

综合1,2我们可以得出,不管是什么自然数,m的二进制形式与轮空计算得到的二进制形式除开第一个进位符与之前的0一致以外,其余的都是互补的,所以两者相加必然是等于比m大的2的幂。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
竞技淘汰赛的过程可以简单描述为:将 n 个选手分成两两组合,每组进行比赛,获胜者晋级下一轮,失败者被淘汰。若有奇数个选手,则有一个选手轮空,直接晋级下一轮。重复上述过程,直至决出冠军。 具体实现上,可以通过一个数组来表示每个选手是否还在比赛,初始时均为 true。每轮比赛结束后,将失败者标记为 false,直至只剩下一个选手为止。 以下是具体的实现代码: ```c #include <stdio.h> #include <stdbool.h> int main() { int n; printf("请输入选手个数:"); scanf("%d", &n); // 初始化选手状态 bool players[n]; for (int i = 0; i < n; i++) { players[i] = true; } // 进行比赛 int round = 1; while (true) { printf("第 %d 轮比赛:\n", round); bool has_winner = false; for (int i = 0; i < n; i += 2) { if (players[i] && players[i + 1]) { printf("选手 %d vs 选手 %d:", i + 1, i + 2); if (rand() % 2 == 0) { // 选手 i 获胜 printf("选手 %d 获胜\n", i + 1); players[i + 1] = false; } else { // 选手 i + 1 获胜 printf("选手 %d 获胜\n", i + 2); players[i] = false; } has_winner = true; } else if (players[i]) { // 选手 i 轮空 printf("选手 %d 轮空\n", i + 1); } else if (players[i + 1]) { // 选手 i + 1 轮空 printf("选手 %d 轮空\n", i + 2); } } if (!has_winner) { // 所有选手均已淘汰,比赛结束 break; } round++; } // 输出冠军 for (int i = 0; i < n; i++) { if (players[i]) { printf("冠军是选手 %d\n", i + 1); break; } } return 0; } ``` 算法复杂度分析: 每轮比赛需要进行 n/2 次比较,因此总比较次数为 n/2 + n/4 + n/8 + ... + 1,这是一个等比数列求和,其结果为 n - 1。因此,总时间复杂度为 O(n)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值