题解:学而思编程 文件传输
【题目来源】
学而思编程:文件传输
【题目描述】
小猴有 \(n\) 台机器需要安装更新文件,开始时仅有 \(1\) 号电脑有更新文件。
在每一个小时内,已安装文件的机器可以通过一条电缆把文件传给另一个未安装文件的电脑。小猴只有 \(k\) 条电缆,也就是说每个小时至多只能有 \(k\) 台电脑进行传输。
请你帮助小猴计算所有机器都接受到更新文件需要多少小时。
【输入】
第一行一个整数 \(T\),表示有 \(T\) 组输入数据;
每组数据一行,两个整数 \(n,k\)。
【输出】
每组数据,输出一个整数。
【输入样例】
4
8 3
6 6
7 1
1 1
【输出样例】
4
3
6
0
【核心思想】
-
问题分析:初始只有 \(1\) 号机器有文件,每小时每台已安装文件的机器可以传给一台未安装的机器,但每小时最多 \(k\) 条电缆(即最多 \(k\) 台新机器接收)。求 \(n\) 台机器全部接收文件所需的最少小时数。这是一个指数增长 + 线性增长的分段问题,关键在于识别增长模式的变化点。
-
算法选择:
- 第一阶段(指数增长):当已安装机器数
sum < k时,每小时可翻倍(每台已安装的传一台新的) - 第二阶段(线性增长):当
sum >= k时,每小时最多新增 \(k\) 台(电缆数限制) - 向上取整:第二阶段计算剩余机器数除以 \(k\) 的向上取整
- 第一阶段(指数增长):当已安装机器数
-
关键步骤:
- 初始化:读取 \(T\)(测试组数)
- 处理每组数据:
- 读取 \(n\)(机器总数)和 \(k\)(电缆数)
sum = 1:当前已安装机器数,t = 0:小时数- 第一阶段(当
sum < k时循环):sum *= 2:每小时翻倍t++
- 第二阶段:
- 若
n <= sum:无需第二阶段(已在第一阶段完成或超过) - 否则:
remain = n - sum:还需安装的机器数t += ceil(remain / k):即(remain + k - 1) / k或代码中的整除判断
- 若
- 输出
t
-
时间/空间复杂度:
- 时间复杂度:\(O(T \times \log k)\),第一阶段最多 \(\log k\) 次翻倍
- 空间复杂度:\(O(1)\),仅需常数变量
-
分段增长的核心思想:
- 指数增长期:当已安装机器少于电缆数时,每台已安装机器都能独立传输,每小时新增数等于当前已安装数,实现翻倍
- 线性增长期:当已安装机器不少于电缆数时,电缆成为瓶颈,每小时固定新增 \(k\) 台
- 变化点识别:
sum >= k是增长模式从指数变为线性的临界点,此时电缆全部用完 - 边界处理:
n = 1时无需传输,t = 0;k >= n时可能在第一阶段就完成 - 适用于"资源限制下的传播/扩散"类问题,识别增长模式的阶段变化是关键
【算法标签】
数学
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 将int定义为long long类型
int T; // 测试用例数量
signed main() // 因为使用了#define int long long,所以用signed代替int
{
cin >> T; // 输入测试用例数量
while (T--) // 处理每个测试用例
{
int n, k; // n: 总数, k: 每次操作的固定增加量
int sum = 1; // 当前总数,初始为1
int t = 0; // 操作次数计数器
cin >> n >> k; // 输入n和k
// 第一阶段:每次翻倍,直到sum >= k
while (sum < k)
{
sum *= 2; // 翻倍操作
t++; // 操作次数加1
}
// 第二阶段:使用每次增加k的操作
if ((n - sum) % k == 0) // 如果能整除
{
t += (n - sum) / k; // 整除,直接相加
}
else if ((n - sum) % k > 0) // 如果不能整除
{
t += (n - sum) / k + 1; // 向上取整
}
cout << t << endl; // 输出总操作次数
}
return 0; // 程序正常结束
}
【运行结果】
4
8 3
4
6 6
3
7 1
6
1 1
0
浙公网安备 33010602011771号