K-P 分解
问题背景
在数论中,我们研究如何将一个正整数 N 分解为其他整数的幂和。本问题关注一种特定的分解方式,称为 K-P 分解。
形式化定义
一个正整数 N 的 K-P 分解,是指找到 K 个正整数 n_1, n_2, ..., n_K,使得它们满足以下等式:
其中:
N,K,P是给定的正整数。n_1, n_2, ..., n_K被称为分解因子。
最优解筛选规则
对于一个给定的 N, K, P,可能存在多种不同的分解方案(即多组不同的分解因子)。我们需要在所有可能的解中,根据以下规则选出唯一的最优解:
-
规则一(因子和最大): 优先选择那个分解因子之和 (
n_1 + n_2 + ... + n_K) 最大的解。 -
规则二(序列最大): 如果在满足规则一的前提下,仍然有多个解(它们的因子和相等且最大),则选择那个分解因子序列在字典序上最大的解。
- 序列比较方式: 将每个解的因子序列按降序排列,然后从左到右逐个比较数字。
任务要求
给定正整数 nValue, kValue, pValue,请找出其 K-P 分解的唯一最优解。
- 如果找到解,则返回其分解因子序列(按降序排列)。
- 如果无解,则返回一个空序列
[]。
输入格式
nValue: 第一个参数,即待分解的正整数N。1 <= nValue <= 400
kValue: 第二个参数,即分解因子的数量K。1 <= kValue <= nValue
pValue: 第三个参数,即次幂P。2 <= pValue <= 7
输出格式
- 一个整数序列(列表或数组),代表最优解的分解因子(已降序排列);若无解,则为空序列。
样例说明
样例 1
-
输入:
nValue = 169,kValue = 5,pValue = 2
-
输出:
[6, 6, 6, 6, 5] -
解释:
- 寻找所有解:
169的 5-2 分解(将169写成5个正整数的平方和)存在多个解,例如:- 解 A:
- 解 B:
- 解 C:
- 应用规则一 (因子和最大): 计算各解的因子和。
- 解 A 的和:
12+4+2+2+1 = 21 - 解 B 的和:
11+6+2+2+2 = 23 - 解 C 的和:
6+6+6+6+5 = 29 - ...(其他解的和可能更小)
- 因子和最大的解是 C,其和为
29。假设这是唯一的最大和解。
- 解 A 的和:
- 最终结果: 输出解 C 的因子序列,降序排列后为
[6, 6, 6, 6, 5]。
- 寻找所有解:
样例 2
-
输入:
nValue = 169,kValue = 167,pValue = 3
-
输出:
[] -
解释: 我们需要将
169分解为167个正整数的立方和。- 所能构成的最小和是所有因子都取
1:。 - 任何其他组合都会使和大于
167(例如,将一个1换成2,和会增加 )。 - 因此,不可能凑出
169这个和。无解,返回空序列。
- 所能构成的最小和是所有因子都取
样例 3
-
输入:
nValue = 28,kValue = 4,pValue = 2
-
输出:
[4, 2, 2, 2] -
解释:
- 寻找所有解:
28的 4-2 分解,存在多个解。 - 应用规则一 (因子和最大): 经过搜索发现,因子和最大的解有两个:
- 解 A: 。因子序列
[4,2,2,2],和为4+2+2+2 = 10。 - 解 B: 。因子序列
[3,3,3,1],和为3+3+3+1 = 10。
- 解 A: 。因子序列
- 应用规则二 (序列最大): 比较这两个和同为
10的解的序列(它们已经是降序)。- 比较
[4, 2, 2, 2]和[3, 3, 3, 1]。 - 从第一位开始比较,
4 > 3。因此,序列[4, 2, 2, 2]更大。
- 比较
- 最终结果: 选择序列
[4, 2, 2, 2]。
- 寻找所有解:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 解决 K-P 分解问题的方案类。
* 核心算法:带有剪枝的深度优先搜索 (DFS)。
*/
public class KPSolution {
// --- 用于存储最终结果的成员变量 ---
// 存储最优解的分解因子序列
private List<Integer> bestSolution = new ArrayList<>();
// 存储最优解的因子之和,初始化为-1表示暂未找到解
private int maxFactorSum = -1;
// --- DFS过程中共享的参数 ---
private int nValue, kValue, pValue;
// 预计算i^P的值,避免重复计算
private List<Integer> powers = new ArrayList<>();
/**
* 主解决函数
*
* @param n K-P分解的目标值N
* @param k K-P分解的因子数量K
* @param p K-P分解的次幂P
* @return 最优解的分解因子序列(降序),若无解则返回空列表
*/
public List<Integer> solve(int n, int k, int p) {
this.nValue = n;
this.kValue = k;
this.pValue = p;
// 预计算所有可能用到的 i^p 的值
int i = 0;
while (true) {
int power = (int) Math.pow(i, p);
if (power > nValue) {
break;
}
powers.add(power);
i++;
}
// 启动DFS搜索。
// tempSolution: 当前正在构建的解
// lastFactorIndex: 上一个选择的因子,用于保证因子降序,避免重复组合
dfs(nValue, kValue, 0, new ArrayList<>(), powers.size() - 1);
return bestSolution;
}
/**
* 深度优先搜索 (DFS) 辅助方法
*
* @param remainingN 剩余需要凑出的值
* @param remainingK 剩余需要选择的因子数量
* @param currentFactorSum 当前已选因子的和
* @param tempSolution 当前正在构建的临时解
* @param lastFactorIndex 上一个选择的因子在powers数组中的索引(用于保证降序)
*/
private void dfs(int remainingN, int remainingK, int currentFactorSum,
List<Integer> tempSolution, int lastFactorIndex) {
// --- Base Case: 成功找到一个解 ---
if (remainingN == 0 && remainingK == 0) {
// 如果当前解的因子和 > 已记录的最大和,则更新最优解
if (currentFactorSum > maxFactorSum) {
maxFactorSum = currentFactorSum;
bestSolution = new ArrayList<>(tempSolution); // 复制一份作为新的最优解
}
// 如果因子和相等,则按字典序比较
else if (currentFactorSum == maxFactorSum) {
if (isLexicographicallyLarger(tempSolution, bestSolution)) {
bestSolution = new ArrayList<>(tempSolution);
}
}
return; // 结束当前路径的搜索
}
// --- 剪枝:无法构成解的失败情况 ---
// 如果剩余目标值<0 或 剩余因子数<=0,说明此路不通
if (remainingN < 0 || remainingK <= 0) {
return;
}
// 如果当前搜索的最大因子索引已经小于1(因子必须是正整数)
if (lastFactorIndex < 1) {
return;
}
// --- 递归搜索 ---
// 从上一个因子开始,向前(向小)尝试所有可能的因子
for (int i = lastFactorIndex; i >= 1; i--) {
// 更多剪枝:如果用当前因子i凑满剩余的k个位置,总和还小于N,说明i太小了,可以直接跳过更小的
if (remainingN < remainingK * powers.get(i)) {
break; // continue 也可以,但因为i是递减的,后续更不可能,可以直接break
}
// 做出选择
tempSolution.add(i);
// 进入下一层递归
dfs(remainingN - powers.get(i),
remainingK - 1,
currentFactorSum + i,
tempSolution,
i); // 下一个因子不能超过当前因子 i
// 撤销选择(回溯),以便尝试其他可能性
tempSolution.remove(tempSolution.size() - 1);
}
}
/**
* 比较两个序列的字典序大小
*/
private boolean isLexicographicallyLarger(List<Integer> list1, List<Integer> list2) {
for (int i = 0; i < list1.size(); i++) {
if (!list1.get(i).equals(list2.get(i))) {
return list1.get(i) > list2.get(i);
}
}
return false; // 如果完全相同,则不算更大
}
// --- 用于测试的 main 方法 ---
public static void main(String[] args) {
KPSolution solution1 = new KPSolution();
System.out.println("Case 1: " + solution1.solve(169, 5, 2)); // 期望输出: [6, 6, 6, 6, 5]
KPSolution solution2 = new KPSolution();
System.out.println("Case 2: " + solution2.solve(169, 167, 3)); // 期望输出: []
KPSolution solution3 = new KPSolution();
System.out.println("Case 3: " + solution3.solve(28, 4, 2)); // 期望输出: [4, 2, 2, 2]
}
}