问题描述
小R正在组织一个比赛,比赛中有 n
支队伍参赛。比赛遵循以下独特的赛制:
- 如果当前队伍数为 偶数,那么每支队伍都会与另一支队伍配对。总共进行
n / 2
场比赛,且产生n / 2
支队伍进入下一轮。 - 如果当前队伍数为 奇数,那么将会随机轮空并晋级一支队伍,其余的队伍配对。总共进行
(n - 1) / 2
场比赛,且产生(n - 1) / 2 + 1
支队伍进入下一轮。
小R想知道在比赛中进行的配对次数,直到决出唯一的获胜队伍为止。
测试样例
样例1:
输入:
n = 7
输出:6
样例2:
输入:
n = 14
输出:13
样例3:
输入:
n = 1
输出:0
思路分析
一、明确问题的递归性质
我们要计算比赛进行到决出唯一获胜队伍时的配对次数。每一轮比赛后队伍数量会发生变化,而这个变化后的情况又可以看作是一个新的、规模更小的同类型问题,这符合递归的特点,即问题可以分解为相似的子问题。
二、确定递归的终止条件
当只剩下一支队伍时,比赛结束,也就不再有配对比赛的情况发生了。所以递归的终止条件就是队伍数量等于 1 时,此时返回的配对次数应该是 0,因为已经决出冠军,不再需要进行新的配对了。
三、分析每一轮比赛的情况并构建递归关系
-
队伍数量为偶数时:
- 如果当前有n支队伍且n为偶数,那么这一轮会进行n/2场比赛(也就是n/2次配对),然后下一轮就会有n/2支队伍继续比赛。
- 所以当n为偶数时,总的配对次数应该是这一轮的配对次数(n/2)加上下一轮比赛(以n/2支队伍进行比赛)所需要的配对次数。这里下一轮比赛所需的配对次数就可以通过递归调用函数来计算,因为它又是一个规模更小的同样问题,只是队伍数量变成了n/2。
-
队伍数量为奇数时:
- 如果当前有n支队伍且n为奇数,那么这一轮会进行(n-1)/2场比赛(也就是(n-1)/2次配对),然后下一轮就会有(n-1)/2+1支队伍继续比赛。
- 所以当n为奇数时,总的配对次数应该是这一轮的配对次数((n-1)/2)加上下一轮比赛(以(n-1)/2+1支队伍进行比赛)所需要的配对次数。同样,下一轮比赛所需的配对次数可以通过递归调用函数来计算,此时队伍数量变成了(n-1)/2+1。
代码
int solution(int n) {
// write code here
int i=0;
if(n==1){
return i;
}
else if(n%2==0){
i=n/2;
return i+solution(i);
}
else{
i=(n-1)/2;
return i+solution(i+1);
}
}
int main() {
printf("%d\n", solution(7) == 6);
printf("%d\n", solution(14) == 13);
printf("%d\n", solution(1) == 0);
return 0;
}
代码解析
-
变量声明:
- 在函数开头声明了一个整型变量
i
,并初始化为 0,这个变量用于记录当前轮次的配对次数。
- 在函数开头声明了一个整型变量
-
递归终止条件(
n == 1
) :- 当传入的队伍数量
n
等于 1 时,意味着已经决出了唯一的获胜队伍,此时不需要再进行任何配对比赛了,所以直接返回i
(其值为 0)。
- 当传入的队伍数量
-
队伍数量为偶数情况(
n % 2 == 0
) :- 当
n
是偶数时,根据赛制,每支队伍都会与另一支队伍配对,总共会进行n / 2
场比赛,也就是n / 2
次配对。所以先将i
赋值为n / 2
。 - 然后,因为这一轮比赛结束后,会有
n / 2
支队伍进入下一轮继续比赛,而下一轮比赛所需要的配对次数同样可以通过调用solution
函数来计算(这就是递归调用),所以最终返回当前轮次的配对次数i
加上下一轮比赛(队伍数量变为n / 2
)所需的配对次数solution(i)
。
- 当
-
队伍数量为奇数情况(
n % 2!= 0
) :- 当
n
是奇数时,根据赛制,将会随机轮空并晋级一支队伍,其余的队伍配对,总共会进行(n - 1) / 2
场比赛,即(n - 1) / 2
次配对。所以先将i
赋值为(n - 1) / 2
。 - 接着,这一轮比赛结束后,会有
(n - 1) / 2 + 1
支队伍进入下一轮继续比赛,同样需要调用solution
函数来计算下一轮比赛(队伍数量变为(n - 1) / 2 + 1
)所需的配对次数(递归调用),最后返回当前轮次的配对次数i
加上下一轮比赛所需的配对次数solution(i + 1)
。
- 当
知识点总结
在解决这个问题中我们用到了递归算法。递归就是在函数的定义中使用函数自身的方法。一个函数直接或间接地调用自身来解决问题,将复杂的大问题逐步分解为与原问题相似但规模更小的子问题,直到子问题简单到可以直接求解(即达到递归终止条件)。递归算法中的关键要素是递归的终止条件和递归关系,这两点弄明白了就可以用递归算法解决基本问题了。