Medium
题目描述
给定两个整数 n 和 k,将数字 n 拆分为恰好 k 个正整数,使得这些整数的乘积等于 n。
返回任意一个拆分,使得任意两个数字之间的最大差值最小。你可以以任意顺序返回结果。
示例 1:
输入:n = 100, k = 2
输出:[10,10]
解释:拆分 [10, 10] 得到 10 * 10 = 100,最大最小差值为 0,这是最小的。
示例 2:
输入: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。
约束条件:
- 4 ≤ n ≤ 10^5
- 2 ≤ k ≤ 5
- k 严格小于 n 的正因数总数
提示:
- 首先,计算 n 的所有正因数并将它们排序到列表 divs 中
- 使用递归搜索 dfs(start, picked, prod, path),从 divs[start…] 中选择下一个因数,将其乘入 prod 并附加到 path,直到有 k 个因数且乘积为 n
- 在搜索过程中,跟踪最小化 max(path) - min(path) 的最佳 path 并返回它
解题思路
这道题要求将数字 n 分解为 k 个正整数的乘积,并使得这些数字之间的最大差值最小。
核心思路:
因数枚举:首先找出 n 的所有因数,因为只有 n 的因数才能参与有效的分解。
回溯搜索:使用深度优先搜索(DFS)来尝试所有可能的分解组合。对于每个位置,我们尝试选择不同的因数,确保剩余的乘积仍能被分解为所需数量的因数。
剪枝优化:
- 当前乘积必须能被 n 整除
- 剩余需要选择的数字数量要合理
- 及时更新最优解,避免不必要的搜索
最优性判断:对于每个完整的分解方案,计算其最大值与最小值的差,保留差值最小的方案。
算法步骤:
- 预处理得到 n 的所有因数并排序
- 使用回溯法枚举所有可能的 k 个因数组合
- 对每个有效组合计算差值,更新最优解
- 返回最优分解方案
由于 k ≤ 5 且 n ≤ 10^5,搜索空间相对较小,回溯法能够有效找到最优解。
代码实现
class Solution {
public:
vector<int> minDifference(int n, int k) {
vector<int> divisors;
for (int i = 1; i * i <= n; i++) {
if (n % i == 0) {
divisors.push_back(i);
if (i != n / i) {
divisors.push_back(n / i);
}
}
}
sort(divisors.begin(), divisors.end());
vector<int> best;
int minDiff = INT_MAX;
vector<int> current;
function<void(int, int, int)> dfs = [&](int idx, int product, int remaining) {
if (remaining == 0) {
if (product == 1) {
int maxVal = *max_element(current.begin(), current.end());
int minVal = *min_element(current.begin(), current.end());
if (maxVal - minVal < minDiff) {
minDiff = maxVal - minVal;
best = current;
}
}
return;
}
if (idx >= divisors.size() || product == 0) return;
for (int i = idx; i < divisors.size(); i++) {
int div = divisors[i];
if (product % div == 0) {
current.push_back(div);
dfs(i, product / div, remaining - 1);
current.pop_back();
}
}
};
dfs(0, n, k);
return best;
}
};
class Solution:
def minDifference(self, n: int, k: int) -> List[int]:
# Find all divisors of n
divisors = []
i = 1
while i * i <= n:
if n % i == 0:
divisors.append(i)
if i != n // i:
divisors.append(n // i)
i += 1
divisors.sort()
best = []
min_diff = float('inf')
def dfs(idx, product, remaining, path):
nonlocal best, min_diff
if remaining == 0:
if product == 1:
max_val = max(path)
min_val = min(path)
if max_val - min_val < min_diff:
min_diff = max_val - min_val
best = path[:]
return
if idx >= len(divisors) or product == 0:
return
for i in range(idx, len(divisors)):
div = divisors[i]
if product % div == 0:
path.append(div)
dfs(i, product // div, remaining - 1, path)
path.pop()
dfs(0, n, k, [])
return best
public class Solution {
public int[] MinDifference(int n, int k) {
List<int> divisors = new List<int>();
for (int i = 1; i * i <= n; i++) {
if (n % i == 0) {
divisors.Add(i);
if (i != n / i) {
divisors.Add(n / i);
}
}
}
divisors.Sort();
List<int> best = new List<int>();
int minDiff = int.MaxValue;
List<int> current = new List<int>();
void Dfs(int idx, int product, int remaining) {
if (remaining == 0) {
if (product == 1) {
int maxVal = current.Max();
int minVal = current.Min();
if (maxVal - minVal < minDiff) {
minDiff = maxVal - minVal;
best = new List<int>(current);
}
}
return;
}
if (idx >= divisors.Count || product == 0) return;
for (int i = idx; i < divisors.Count; i++) {
int div = divisors[i];
if (product % div == 0) {
current.Add(div);
Dfs(i, product / div, remaining - 1);
current.RemoveAt(current.Count - 1);
}
}
}
Dfs(0, n, k);
return best.ToArray();
}
}
var minDifference = function(n, k) {
function getDivisors(num) {
const divisors = [];
for (let i = 1; i <= Math.sqrt(num); i++) {
if (num % i === 0) {
divisors.push(i);
if (i !== num / i) {
divisors.push(num / i);
}
}
}
return divisors.sort((a, b) => a - b);
}
function backtrack(remaining, factorsLeft, currentFactors) {
if (factorsLeft === 0) {
if (remaining === 1) {
return [...currentFactors];
}
return null;
}
if (factorsLeft === 1) {
currentFactors.push(remaining);
return [...currentFactors];
}
const divisors = getDivisors(remaining);
let bestResult = null;
let minDiff = Infinity;
for (const divisor of divisors) {
if (divisor > remaining) break;
currentFactors.push(divisor);
const result = backtrack(remaining / divisor, factorsLeft - 1, currentFactors);
if (result) {
const diff = Math.max(...result) - Math.min(...result);
if (diff < minDiff) {
minDiff = diff;
bestResult = [...result];
}
}
currentFactors.pop();
}
return bestResult;
}
return backtrack(n, k, []);
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(d^k),其中 d 是 n 的因数个数,通常 d = O(√n),k ≤ 5 |
| 空间复杂度 | O(d + k),用于存储因数列表和递归栈空间 |