大家好,我是老学长。
最近英雄联盟S13比赛正在如火如荼的进行之中,其中有一个非常有趣的赛制,叫做瑞士轮。
瑞士轮把16支队伍放在一起比赛。这样必然会生成8支1-0战绩的队伍和8支0-1的队伍。相同战绩的队伍和相同战绩的队伍对战。直到获胜3场晋级或者3场失利淘汰,循环才结束。这样的赛制安排可以保证对局的队伍能够保持近似相同的水平,不会导致碾压式的枯燥对局产生。相近队伍相比会让比赛更有看点。
看到瑞士轮赛制,我头中浮现出一个算法问题。一开始有16支0-0战绩的队伍,现在我想求1-1战绩的队伍,应该怎么求?
从图中我们看出 1-1战绩的队伍是由两个部分组成的,分别是1-0队伍的一半和0-1队伍的一半。
1-0 和 0-1 又分别是 0-0 的一半
通过递归我们可以求出1-1战绩队伍的数量。
那如果我想求a-b战绩的队伍应该怎么办?
我们虽然不能直面求a-b战绩的队伍,但是我们可以得到a-b战绩的队伍应该是由(a-1) - b 战绩队伍的一半 和 a - (b-1) 战绩队伍的一半 获得的。这样我们就可以列出递归表达式。
对于0 - b 和 a - 0 战绩的队伍,由于不可能由负战绩的队伍,所以只有一部分。
递归的出口就是 0 - 0队伍,也就是参加队伍的数量。
这样我们就可以写出递归表达式
代码如下
#include<iostream>
using namespace std;
int dp(int a,int b){
if(a == 0 && b == 0) return 16;
if(a == 0){
return dp(a,b-1) / 2;
}
if(b == 0){
return dp(a-1,b) / 2;
}
int part_a = dp(a-1,b) / 2;
int part_b = dp(a,b-1) / 2;
return part_a + part_b;
}
int main(){
cout<<dp(2,1);
return 0;
}
这种方法就是递归算法,运用到了动态规划的思想。a-b战绩的队伍可以划分成两部分,如果之前我们将两部分算出来储存到数组中,就可以直接进行调用。大大减少了我们的计算量。
真是没想到一个小小的世界赛,居然蕴含了这么巧妙的算法。所以要好好学习数学和算法,要不然说是英雄联盟老玩家,连世界赛的赛制都看不懂就尴尬了。