题目链接
题目描述
给定一个正整数 ,初始值为
。你可以进行若干次操作:选择一个整数
,支付
的代价,将当前值变为其与
的商。要求每次操作的
都必须是当前值的因子。请计算将
恰好变为
所需要支付的最小总代价。
解题思路
本题的本质是一个最优化问题,我们可以将其转化为一个经典的数论问题。
-
问题转化:假设我们进行了一系列操作,选择的除数依次为
。那么操作过程可以表示为:
这说明
。我们的目标是最小化总代价,即最小化
。
因此,问题等价于:将数
分解为若干个大于等于2的整数的乘积,并求这些整数的最小和。
-
核心洞察:考虑任意一个合数因子
。假设
,其中
。在我们的因子和中,我们可以用
来替换
。由于
,我们可以推断
(仅当
时取等号,其他情况均有
)。这意味着,将任何合数因子进一步分解为其因子,总能使因子之和变得更小或保持不变。
-
最优策略:为了使因子之和最小,我们必须将
分解到不能再分解为止,也就是将
完全分解为其质因数。最小的总代价就是
所有质因数的总和。
-
算法实现:
- 我们使用试除法来找出
的所有质因数。
- 从
开始循环,直到
。
- 对于每个
,用一个内部
while
循环来判断是否能被
整除。只要能整除,就将
加入总代价中,并更新
为
。
- 外层循环结束后,如果
仍然大于
,说明剩下的这个
本身也是一个质因数,需要将其加入总代价。
- 我们使用试除法来找出
代码
#include <iostream>
#include <cmath>
using namespace std;
int main() {
long long n;
cin >> n;
long long total_cost = 0;
for (long long i = 2; i * i <= n; ++i) {
while (n % i == 0) {
total_cost += i;
n /= i;
}
}
if (n > 1) {
total_cost += n;
}
cout << total_cost << endl;
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long n = sc.nextLong();
long total_cost = 0;
for (long i = 2; i * i <= n; ++i) {
while (n % i == 0) {
total_cost += i;
n /= i;
}
}
if (n > 1) {
total_cost += n;
}
System.out.println(total_cost);
}
}
import math
n = int(input())
total_cost = 0
i = 2
temp_n = n
while i * i <= temp_n:
while temp_n % i == 0:
total_cost += i
temp_n //= i
i += 1
if temp_n > 1:
total_cost += temp_n
print(total_cost)
算法及复杂度
- 算法:数论、质因数分解
- 时间复杂度:算法的核心是试除法,循环最多执行到
次。因此,时间复杂度为
。
- 空间复杂度:算法仅使用了少数变量来存储中间结果,因此空间复杂度为
。