首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >2026-02-01:K 因数分解。用go语言,给定两个正整数 n 和 k,要把 n 表示为恰好 k 个正整数相乘的结果(因子允许重复)。在所有满足乘积

2026-02-01:K 因数分解。用go语言,给定两个正整数 n 和 k,要把 n 表示为恰好 k 个正整数相乘的结果(因子允许重复)。在所有满足乘积

作者头像
福大大架构师每日一题
发布2026-02-09 14:43:52
发布2026-02-09 14:43:52
930
举报

2026-02-01:K 因数分解。用go语言,给定两个正整数 n 和 k,要把 n 表示为恰好 k 个正整数相乘的结果(因子允许重复)。在所有满足乘积为 n 的 k 元组中,挑出一组使其最大值与最小值之差最小。返回这样的一组数,输出顺序不限。

4 <= n <= 100000。

2 <= k <= 5。

k 严格小于 n 的正因数的总数。

输入:n = 44, k = 3。

输出:[2,2,11]。

解释:

分割方案 [1, 1, 44] 的差值为 43。

分割方案 [1, 2, 22] 的差值为 21。

分割方案 [1, 4, 11] 的差值为 10。

分割方案 [2, 2, 11] 的差值为 9。

因此,[2, 2, 11] 是最优分割方案,其差值最小,为 9。

题目来自力扣3669。

算法步骤详解

1. 预处理阶段:构建全局因子表

  • 目的:为了在后续搜索过程中能快速获取任意数字 n 的所有因子,算法在程序开始时进行了一次性的预处理。
  • 过程:代码定义了一个全局变量 divisors,它是一个长度为 mx (100,001) 的切片,每个元素 divisors[i] 是一个整数切片,用于存储数字 i 的所有正因子。
  • 实现方法:使用两层循环。外层循环 (i) 遍历从1到 mx-1 的所有数字,内层循环 (j) 枚举 i 的所有倍数(j = i, 2i, 3i, ...,且 j < mx)。对于每一个 j,将 i 加入到 divisors[j] 中,因为 ij 的一个因子。
  • 结果:预处理完成后,对于任意数字 n (n < mx),都可以通过 divisors[n] 立即获得其所有因子,且这些因子是按升序排列的(因为 i 是从小到大遍历的)。

2. 深度优先搜索 (DFS)

  • 核心任务:通过DFS遍历所有可能的 k 因子分解方案,并记录最优解。
  • 函数定义dfs(i, n) 是这个搜索过程的核心。
    • 参数 i:表示当前已经确定了 i 个因子(即 path[0]path[i-1] 已填充)。
    • 参数 n:表示剩余需要被分解的数值,即 n = 初始n / (path[0] * path[1] * ... * path[i-1])
  • 搜索过程
    • 终止条件:当已经确定了k-1个因子时(即i == k-1),最后一个因子必须是当前剩余的n值本身(因为所有因子相乘必须等于原始的n)。此时,当前分解方案path已经完整。计算该方案中最大值(即n)与最小值(即path[0])的差值。如果这个差值比之前记录的最小差值minDiff更小,则更新minDiff并将当前的path复制到结果ans 中。
    • 枚举因子:对于非终止情况,算法枚举当前剩余数值 n 的所有因子 d(直接从预处理好的 divisors[n] 中获取)。
    • 剪枝策略:为了减少不必要的搜索,算法采用了两个关键的剪枝条件:
      1. 1. 因子大小限制:如果 d * d > n,说明 d 已经大于 sqrt(n),再枚举下去得到的因子会和之前枚举的因子对称(例如,对于 n=44,枚举完 d=2d=22 是等价的),因此可以提前终止循环 (break)。
      2. 2. 差值优化:如果已经确定了至少一个因子(i > 0),并且当前枚举的因子 d 与已确定的最小因子 path[0] 的差值已经大于或等于当前已知的最小差值 minDiff,那么即使继续分解下去,最终方案的差值也不可能小于 minDiff,因此也可以跳过后续更大的因子 (break)。
    • 合法性检查与递归:对于枚举的因子 d,需要满足非递减顺序的要求(即 i == 0 时第一个因子可以任意选,否则 d 必须大于等于前一个因子 path[i-1])。如果满足,则将 d 放入 path[i] 的位置,然后递归调用 dfs(i+1, n/d),继续分解剩下的数值。
3. 主函数与初始化
  • 启动minDifference 函数是入口。它初始化用于记录最小差值的变量 minDiff 和一个长度为 k 的切片 path 用于记录当前搜索路径。
  • 开始搜索:调用 dfs(0, n),从第0个因子开始,需要分解的数值为原始的 n
  • 返回结果:DFS完成后,结果切片 ans 中存储的就是找到的最优分解方案。

复杂度分析

时间复杂度

  • 预处理阶段:外层循环运行 O(mx) 次,内层循环运行 O(mx/i) 次(对于每个 i)。总操作数约为 mx * (1 + 1/2 + 1/3 + ... + 1/mx),这个求和是调和级数,其渐进复杂度为 O(mx log mx),其中 mx = 100,001
  • DFS阶段:这是算法的主要开销。最坏情况下需要枚举所有可能的分解方案。对于n的因数分解,方案数与其因子个数和k的值有关。由于k最大为5,且n <= 100,000,DFS的深度很浅(最多5层),但每一层分支较多。其时间复杂度可以近似看作O(d^k),其中dn的因子个数。在n的范围内,一个数的因子个数d 通常不会非常大(几百量级),且剪枝策略有效减少了实际搜索空间。
  • 总复杂度:由预处理和DFS两部分组成。预处理是 O(mx log mx)。DFS在最坏情况下可能达到 O(d^k),但由于 k 很小且剪枝有效,实际运行效率尚可。综合考虑,总的时间复杂度主要由DFS部分主导。

额外空间复杂度

  • 预处理存储divisors 数组需要 O(mx * d_avg) 的空间,其中 d_avg 是平均因子个数。对于 mx=100,001,这大约是几MB的量级。
  • DFS递归栈:递归深度最大为 k (<=5),每一层递归的局部变量开销是常数。因此栈空间复杂度为 O(k)
  • 路径存储pathans 切片的大小均为 k,空间复杂度为 O(k)
  • 总额外空间复杂度:主要由预处理阶段的 divisors 数组决定,为 O(mx * d_avg)

总结

该算法通过预处理构建因子表来加速因子枚举,并利用深度优先搜索结合有效的剪枝策略(因子大小限制、差值优化、非递减顺序)来寻找最优解。其优势在于能准确找到差值最小的分解方案,但由于问题本身的组合性质,在最坏情况下时间复杂度仍可能较高,好在题目约束 k 很小,使得算法在实践中可行。

Go完整代码如下:

.

代码语言:javascript
复制
package main

import (
    "fmt"
    "math"
    "slices"
)

const mx = 100_001

var divisors [mx][]int

func init() {
    // 预处理每个数的因子
    for i := 1; i < mx; i++ {
        for j := i; j < mx; j += i { // 枚举 i 的倍数 j
            divisors[j] = append(divisors[j], i) // i 是 j 的因子
        }
    }
}

func minDifference(n, k int) (ans []int) {
    minDiff := math.MaxInt
    path := make([]int, k)
    var dfs func(int, int)
    dfs = func(i, n int) {
        if i == k-1 {
            // path[0] 最小,n 最大
            if n-path[0] < minDiff {
                minDiff = n - path[0]
                path[i] = n
                ans = slices.Clone(path)
            }
            return
        }
        for _, d := range divisors[n] { // 枚举 n 的因子 d
            if d*d > n || i > 0 && d-path[0] >= minDiff {
                break
            }
            if i == 0 || d >= path[i-1] {
                path[i] = d // 直接覆盖,无需恢复现场
                dfs(i+1, n/d)
            }
        }
    }
    dfs(0, n)
    return
}

func main() {
    n := 44
    k := 3
    result := minDifference(n, k)
    fmt.Println(result)
}
在这里插入图片描述
在这里插入图片描述

Python完整代码如下:

.

代码语言:javascript
复制
# -*-coding:utf-8-*-

import math
import sys
from typing import List

MX = 100_001

# 预处理每个数的因子
divisors = [[] for _ in range(MX)]
for i in range(1, MX):
    for j in range(i, MX, i):  # 枚举 i 的倍数 j
        divisors[j].append(i)  # i 是 j 的因子

def min_difference(n: int, k: int) -> List[int]:
    min_diff = float('inf')
    path = [0] * k
    ans = []
    
    def dfs(i: int, current_n: int):
        nonlocal min_diff, ans
        
        if i == k - 1:
            # path[0] 最小,current_n 最大
            if current_n - path[0] < min_diff:
                min_diff = current_n - path[0]
                path[i] = current_n
                ans = path.copy()
            return
        
        # 枚举 current_n 的因子 d
        for d in divisors[current_n]:
            if d * d > current_n or (i > 0 and d - path[0] >= min_diff):
                break
            
            if i == 0 or d >= path[i - 1]:
                path[i] = d  # 直接覆盖,无需恢复现场
                dfs(i + 1, current_n // d)
    
    dfs(0, n)
    return ans

if __name__ == "__main__":
    n = 44
    k = 3
    result = min_difference(n, k)
    print(result)
在这里插入图片描述
在这里插入图片描述

C++完整代码如下:

.

代码语言:javascript
复制
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
#include <cmath>
using namespace std;

constint MX = 100001;
vector<int> divisors[MX];

// 预处理每个数的因子
void init() {
    for (int i = 1; i < MX; i++) {
        for (int j = i; j < MX; j += i) { // 枚举 i 的倍数 j
            divisors[j].push_back(i);     // i 是 j 的因子
        }
    }
}

void dfs(int i, int n, int k, int& minDiff, vector<int>& path, vector<int>& ans) {
    if (i == k - 1) {
        // path[0] 最小,n 最大
        if (n - path[0] < minDiff) {
            minDiff = n - path[0];
            path[i] = n;
            ans = path;
        }
        return;
    }

    for (int d : divisors[n]) { // 枚举 n 的因子 d
        if (d * d > n || (i > 0 && d - path[0] >= minDiff)) {
            break;
        }
        if (i == 0 || d >= path[i - 1]) {
            path[i] = d; // 直接覆盖,无需恢复现场
            dfs(i + 1, n / d, k, minDiff, path, ans);
        }
    }
}

vector<int> minDifference(int n, int k) {
    vector<int> ans;
    int minDiff = INT_MAX;
    vector<int> path(k);

    dfs(0, n, k, minDiff, path, ans);
    return ans;
}

int main() {
    init();

    int n = 44;
    int k = 3;
    vector<int> result = minDifference(n, k);

    for (int num : result) {
        cout << num << " ";
    }
    cout << endl;

    return0;
}
在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-01-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 福大大架构师每日一题 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 算法步骤详解
    • 1. 预处理阶段:构建全局因子表
    • 2. 深度优先搜索 (DFS)
  • 复杂度分析
    • 时间复杂度
    • 额外空间复杂度
  • 总结
  • Go完整代码如下:
  • Python完整代码如下:
  • C++完整代码如下:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档