这里写自定义目录标题
更多精彩内容
这里是带你游历编程世界的Dashcoding编程社,我是Dash/北航硕士/ICPC区域赛全国排名30+/给你呈现我们眼中的世界!
256题算法特训课,帮你斩获大厂60W年薪offer
原题
腾讯校招真题最大公约数
B站动画详解
问题分析
这道题的核心在于通过有限的 k k k 次操作,最大化两个整数 a a a 和 b b b 的最大公约数 (GCD, Greatest Common Divisor)。具体来说,问题要求每次可以将 a a a 或 b b b 增加1,然后问在进行 k k k 次操作后,如何使 a a a 和 b b b 的 GCD 最大。GCD 的性质决定了当两个数之间差距越小且有更多的公共因子时,GCD 越大。因此,最优策略是通过调整 a a a 和 b b b 的值来找到差距最小且GCD最大的情况。
在实际操作中,可以枚举每一种可能的操作组合,即 a + i a + i a+i 和 b + ( k − i ) b + (k - i) b+(k−i),其中 i i i 代表对 a a a 进行的自增次数, k − i k - i k−i 代表对 b b b 进行的自增次数。对每种组合计算 GCD 并取最大值即为所求。
为了优化计算过程,可以利用欧几里得算法快速计算两个数的 GCD,从而确保在枚举所有可能操作组合时,效率仍然较高。
思路分析
这道题的解法主要依赖枚举优化和GCD的性质。具体实现思路如下:
- 枚举 i i i 的取值范围从 0 0 0 到 k k k,表示对 a a a 进行了 i i i 次自增。
- 对于每个 i i i,计算 a + i a + i a+i 和 b + ( k − i ) b + (k - i) b+(k−i) 的 GCD。
- 在所有枚举的结果中,取最大的 GCD 即为答案。
这个算法的核心是利用了GCD的性质,以及对 k k k 次操作进行枚举。尽管最坏情况下需要枚举 k + 1 k + 1 k+1 种情况,但由于 k k k 的最大值为 100 , 000 100,000 100,000,而 GCD 的计算效率很高 (时间复杂度为 O ( log ( min ( a , b ) ) ) O(\log(\min(a, b))) O(log(min(a,b)))),因此整体算法在常数因子较低的情况下仍然可以通过。
算法的时间复杂度为 O ( T × k × log ( min ( a , b ) ) ) O(T \times k \times \log(\min(a, b))) O(T×k×log(min(a,b))),其中 T T T 是测试用例的数量。尽管 k k k 的范围较大,但在合理的优化下,算法在规定的输入范围内仍然能保证较快的执行速度。
算法实现
代码主要分为两部分:首先定义了一个用于计算 GCD 的函数 gcd(int a, int b),该函数采用欧几里得算法,通过不断取余直到余数为 0 来获取 GCD。接着在主函数中,读取测试用例的数量 t t t 并逐个处理。
对于每个测试用例,读取 a a a、 b b b 和 k k k 的值,并通过循环枚举所有可能的 i i i。对于每种 i i i,计算 a + i a + i a+i 和 b + ( k − i ) b + (k - i) b+(k−i) 的 GCD,并在结果中选择最大值。
最后输出每个测试用例的结果。
代码详解
标准代码程序
C++代码
#include <iostream>
using namespace std;
int gcd(int a, int b)
{
while (b != 0)
{
int tmp = a;
a = b;
b = tmp % b;
}
return a;
}
int main()
{
int t;
cin >> t;
for (int i = 0; i < t; ++i)
{
int a, b, k;
cin >> a >> b >> k;
int ans = 0;
for (int i = 0; i < k; ++i)
{
ans = max(gcd(a + i, b + k - i), ans);
}
cout << ans << "\n";
}
return 0;
}
Java代码
import java.util.Scanner;
public class Main {
public static int gcd(int a, int b) {
while (b != 0) {
int tmp = a;
a = b;
b = tmp % b;
}
return a;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int t = scanner.nextInt();
for (int i = 0; i < t; ++i) {
int a = scanner.nextInt();
int b = scanner.nextInt();
int k = scanner.nextInt();
int ans = 0;
for (int j = 0; j < k; ++j) {
ans = Math.max(gcd(a + j, b + k - j), ans);
}
System.out.println(ans);
}
scanner.close();
}
}
Python代码
import math
def solve():
t = int(input())
for _ in range(t):
a, b, k = map(int, input().split())
ans = 0
for i in range(k):
ans = max(math.gcd(a + i, b + k - i), ans)
print(ans)
if __name__ == "__main__":
solve()
Javascript代码
function gcd(a, b) {
while (b !== 0) {
let tmp = a;
a = b;
b = tmp % b;
}
return a;
}
function solve() {
const input = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
let t = null;
const data = [];
input.on('line', (line) => {
if (t === null) {
t = parseInt(line);
} else {
data.push(line.split(' ').map(Number));
if (data.length === t) {
input.close();
}
}
});
input.on('close', () => {
data.forEach(([a, b, k]) => {
let ans = 0;
for (let i = 0; i < k; i++) {
ans = Math.max(gcd(a + i, b + k - i), ans);
}
console.log(ans);
});
});
}
solve();
复杂度分析
时间复杂度分析
给定测试用例数量 T T T、两个整数 a a a 和 b b b 以及操作次数 k k k,算法的核心是枚举从 i = 0 i = 0 i=0 到 i = k i = k i=k 的所有可能操作组合。对于每一个操作组合,我们都需要计算两个数的最大公约数(GCD)。因此,算法的时间复杂度主要由以下几部分组成:
-
枚举操作组合的复杂度:算法需要枚举 i i i 从 0 0 0 到 k k k 的所有值,所以这一部分的时间复杂度是 O ( k ) O(k) O(k)。
-
GCD 计算的复杂度:对于每个枚举的组合,需要计算 a + i a + i a+i 和 b + ( k − i ) b + (k - i) b+(k−i) 的 GCD。使用欧几里得算法计算 GCD 的时间复杂度是 O ( log ( min ( a , b ) ) ) O(\log(\min(a, b))) O(log(min(a,b)))。
-
总复杂度:在最坏情况下,测试用例数量 T T T 是 20 20 20,每个测试用例中 k k k 的最大值是 100 , 000 100,000 100,000。因此,总时间复杂度是 O ( T × k × log ( min ( a , b ) ) ) O(T \times k \times \log(\min(a, b))) O(T×k×log(min(a,b)))。用具体的值替代表示,总复杂度最多为 O ( 20 × 100 , 000 × log ( 100 , 000 ) ) O(20 \times 100,000 \times \log(100,000)) O(20×100,000×log(100,000)),大约为 O ( 20 × 100 , 000 × 17 ) = 34 , 000 , 000 O(20 \times 100,000 \times 17) = 34,000,000 O(20×100,000×17)=34,000,000 次操作。
虽然计算量较大,但由于 GCD 计算的效率较高(欧几里得算法的常数因子很小),在规定的输入范围内,这个复杂度是可以接受的,尤其在 C++、Java 等高效语言中。
空间复杂度分析
空间复杂度主要考虑算法在执行过程中使用的额外内存,包括变量、数据结构、递归调用栈等。
-
输入数据占用的空间:算法中需要存储 T T T 组测试用例,每个测试用例包含 a a a、 b b b 和 k k k 这三个整数。因为 T T T 的最大值是 20,整数的大小为 O ( 1 ) O(1) O(1),所以这部分的空间复杂度是 O ( T ) O(T) O(T),在最坏情况下是 O ( 20 ) O(20) O(20),这属于常量级别的空间占用。
-
枚举操作时的临时变量:在枚举 i i i 的过程中,主要存储的是中间结果 a + i a + i a+i、 b + ( k − i ) b + (k - i) b+(k−i) 以及计算得到的 GCD 值。这些都是固定的整数,占用常量级别的空间,因此这一部分的空间复杂度是 O ( 1 ) O(1) O(1)。
-
GCD 计算的空间:欧几里得算法的 GCD 计算是一个递归过程,但在实际实现中通常使用迭代版本,递归深度为 O ( log ( min ( a , b ) ) ) O(\log(\min(a, b))) O(log(min(a,b))),也就是说,GCD 计算的空间复杂度为 O ( log ( min ( a , b ) ) ) O(\log(\min(a, b))) O(log(min(a,b)))。但是因为我们采用了迭代方法,实际上没有额外的递归栈空间,所以这部分的空间占用也只是 O ( 1 ) O(1) O(1)。
-
输出数据的空间:输出每个测试用例的结果是一个整数,因此总共输出 T T T 个结果,占用 O ( T ) O(T) O(T) 的空间,最坏情况下为 O ( 20 ) O(20) O(20),仍然是常量级别的。
总结
这道题目要求通过最多 k k k 次操作来最大化两个整数 a a a 和 b b b 的最大公约数(GCD)。为了实现这一目标,算法采用了枚举优化的策略,通过遍历每一种可能的操作组合(即 a + i a + i a+i 和 b + ( k − i ) b + (k - i) b+(k−i)),计算相应的 GCD,并选择最大的结果作为最终答案。
在算法分析中,我们得出了时间复杂度为 O ( T × k × log ( min ( a , b ) ) ) O(T \times k \times \log(\min(a, b))) O(T×k×log(min(a,b))),其中 T T T 是测试用例的数量, k k k 是允许的操作次数, log ( min ( a , b ) ) \log(\min(a, b)) log(min(a,b)) 是计算 GCD 的复杂度。虽然 k k k 的最大值达到 100,000,但由于欧几里得算法计算 GCD 的效率极高,因此在实际输入范围内,该算法的运行效率可以接受。
空间复杂度方面,由于算法中只使用了少量的额外空间,主要是常量级别的变量,因此空间复杂度为 O ( 1 ) O(1) O(1),除去输入输出外不需要额外的内存。
综上所述,这道题的解法不仅简洁有效,且在高效实现下可以处理大规模输入。对于需要在有限操作次数内寻找最优解的问题,这样的枚举优化策略是十分值得参考的。