题目链接

最小乘积代价和

题目描述

给定一个正整数 ,初始值为 。你可以进行若干次操作:选择一个整数 ,支付 的代价,将当前值变为其与 的商。要求每次操作的 都必须是当前值的因子。请计算将 恰好变为 所需要支付的最小总代价。

解题思路

本题的本质是一个最优化问题,我们可以将其转化为一个经典的数论问题。

  1. 问题转化:假设我们进行了一系列操作,选择的除数依次为 。那么操作过程可以表示为:

    这说明 。我们的目标是最小化总代价,即最小化

    因此,问题等价于:将数 分解为若干个大于等于2的整数的乘积,并求这些整数的最小和。

  2. 核心洞察:考虑任意一个合数因子 。假设 ,其中 。在我们的因子和中,我们可以用 来替换 。由于 ,我们可以推断 (仅当 时取等号,其他情况均有 )。这意味着,将任何合数因子进一步分解为其因子,总能使因子之和变得更小或保持不变。

  3. 最优策略:为了使因子之和最小,我们必须将 分解到不能再分解为止,也就是将 完全分解为其质因数。最小的总代价就是 所有质因数的总和。

  4. 算法实现

    • 我们使用试除法来找出 的所有质因数。
    • 开始循环,直到
    • 对于每个 ,用一个内部 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)

算法及复杂度

  • 算法:数论、质因数分解
  • 时间复杂度:算法的核心是试除法,循环最多执行到 次。因此,时间复杂度为
  • 空间复杂度:算法仅使用了少数变量来存储中间结果,因此空间复杂度为