分糖果,圣诞节到了,城堡里有k个小朋友,圣诞老人魔力袋里带了n件无差别的小礼物,请圣诞老人处理,将n个无差别的礼物分给k个小朋友的分法问题,给定n和k,输出总分法总数,并枚举所有的分法。其中 | 作为分隔符,每个小朋友的糖果用星号表示。
示例:3个糖果分给2个小朋友。总共有4种分法,枚举所有的分法如下:
*** |
** |*
* |**
| ***
java代码实现如下:
java
/**
* 计算将n个无差别糖果分给k个小朋友的分法总数(组合数学中的"stars and bars"问题)
* 公式:C(n + k - 1, k - 1)
*/
public static int countDistributionWays(int n, int k) {
// 使用组合数公式 C(n + k - 1, k - 1)
return combination(n + k - 1, k - 1);
}
java
/**
* 计算组合数 C(n, k)
*/
private static int combination(int n, int k) {
if (k < 0 || k > n) return 0;
if (k == 0 || k == n) return 1;
// 使用递推公式 C(n, k) = C(n-1, k-1) + C(n-1, k)
int[][] dp = new int[n + 1][k + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= Math.min(i, k); j++) {
if (j == 0 || j == i) {
dp[i][j] = 1;
} else {
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
}
}
}
return dp[n][k];
}
java
/**
* 枚举所有分法
*/
public static List<List<Integer>> enumerateDistributions(int n, int k) {
List<List<Integer>> result = new ArrayList<>();
List<Integer> current = new ArrayList<>();
// 初始化当前分配方案
for (int i = 0; i < k; i++) {
current.add(0);
}
backtrack(n, k, 0, 0, current, result);
return result;
}
java
/**
* 回溯法生成所有分配方案
*/
private static void backtrack(int n, int k, int index, int sum,
List<Integer> current, List<List<Integer>> result) {
// 如果已经分配完所有糖果且分配到所有小朋友
if (index == k) {
if (sum == n) {
result.add(new ArrayList<>(current));
}
return;
}
// 当前小朋友可以分得的糖果数(从0到剩余糖果数)
int remaining = n - sum;
for (int i = 0; i <= remaining; i++) {
current.set(index, i);
backtrack(n, k, index + 1, sum + i, current, result);
}
}
java
/**
* 打印所有分配方案
*/
public static void printDistributions(List<List<Integer>> distributions) {
for (int i = 0; i < distributions.size(); i++) {
List<Integer> dist = distributions.get(i);
StringBuilder format = new StringBuilder();
for (int j = 0; j < dist.size(); j++) {
int candyCount = dist.get(j);
// 添加星号
format.append("*".repeat(candyCount));
// 添加分隔符(除了最后一个小朋友)
if (j < dist.size() - 1) {
format.append("|");
}
}
System.out.println(format.toString());
}
}