给定一个由连续正整数组成的数组N和一个目标整数M,计算使用以下两种方式构建M的方法总数:
示例1:
输入:N = [1, 2], M = 4 输出:4 解释: 方法1:1+1+1+1 方法2:1+1+2 方法3:2+2 方法4:使用x=0(如果允许),加上1+1+2(但通常x最小为1)
示例2:
输入:N = [2, 5], M = 4 输出:1 解释: 方法1:2+2=4 (不能使用x=1,因为1+3=4但3不在N中)
这个问题可以分解为两个子问题:
我们使用动态规划来解决这个组合问题:
javapublic static int countAssemblyMethods(int[] N, int M) {
// 情况1:仅使用N中的元素(可重复使用)
int ways1 = countUnboundedSubsetSum(N, M);
// 情况2:使用一个不在N中的元素x(x < N[0]),再加上N中的元素
int ways2 = 0;
if (N.length > 0 && N[0] > 1) {
for (int x = 1; x < N[0]; x++) {
if (M > x) {
ways2 += countUnboundedSubsetSum(N, M - x);
} else if (M == x) {
ways2 += 1; // 单独使用x的情况
}
}
}
return ways1 + ways2;
}
javaprivate static int countUnboundedSubsetSum(int[] nums, int sum) {
int[] dp = new int[sum + 1];
dp[0] = 1; // 和为0只有一种方法:不选任何元素
for (int num : nums) {
for (int i = num; i <= sum; i++) {
dp[i] += dp[i - num];
}
}
return dp[sum];
}
javaimport java.util.Scanner;
public class ArrayAssembly {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取输入数组N
String[] nStr = scanner.nextLine().split(" ");
int[] N = new int[nStr.length];
for (int i = 0; i < nStr.length; i++) {
N[i] = Integer.parseInt(nStr[i]);
}
// 读取整数M
int M = Integer.parseInt(scanner.nextLine());
// 计算并输出组装方法数量
System.out.println(countAssemblyMethods(N, M));
scanner.close();
}
public static int countAssemblyMethods(int[] N, int M) {
// 情况1:仅使用N中的元素(可重复使用)
int ways1 = countUnboundedSubsetSum(N, M);
// 情况2:使用一个不在N中的元素x(x < N[0]),再加上N中的元素(可重复)
int ways2 = 0;
if (N.length > 0 && N[0] > 1) {
for (int x = 1; x < N[0]; x++) {
if (M > x) {
ways2 += countUnboundedSubsetSum(N, M - x);
} else if (M == x) {
ways2 += 1; // 单独使用x的情况
}
}
}
return ways1 + ways2;
}
// 允许重复使用元素的子集和计算(完全背包)
private static int countUnboundedSubsetSum(int[] nums, int sum) {
int[] dp = new int[sum + 1];
dp[0] = 1;
for (int num : nums) {
for (int i = num; i <= sum; i++) {
dp[i] += dp[i - num];
}
}
return dp[sum];
}
}
这类组合问题在实际中有广泛应用,例如:
通过将问题分解为两个子问题,并运用动态规划技术,我们能够高效地计算出所有可能的组合方式。关键在于:
这种解法不仅适用于本问题,其思路也可以推广到其他类似的组合问题中。
思考题:如果题目改为不允许重复使用N中的元素,算法应该如何修改?欢迎在评论区分享你的解决方案!