【算法习题】正整数数组中和为sum的任意个数的组合数

1、递归实现(参考:https://blog.csdn.net/hit_lk/article/details/53967627)

 1 public class Test {
 2  
 3     @org.junit.Test
 4     public void test() {
 5         System.out.println("方案数:" + getAllSchemeNum(new int[]{ 5, 5, 5, 2, 3 }, 15));
 6     } // out : 方案数:4
 7 
 8     /**
 9      * 从数组中选择和为sum的任意个数的组合数
10      */
11     public static int getAllSchemeNum(int[] arr, int sum) {
12         int count = 0;
13         // 将 选择一个数的组合数、选择两个数的组合数、...选择n个数的组合数 相加
14         for (int numToSelect = 1; numToSelect <= arr.length; numToSelect++) {
15             count += getSchemeNumByNumToSelect(arr, numToSelect, sum, 0);
16         }
17         return count;
18     }
19     
20     /**
21      * 求【从数组的[arr[index], arr[length-1]]片段中获取和为sumToSelect的numToSelect个数】的方案数
22      * @param arr 数组
23      * @param numToSelect 还需要选择的数的个数
24      * @param sumToSelect 还需要选择数之和
25      * @param index 可选的范围的左边界
26      * @return
27      */
28     public static int getSchemeNumByNumToSelect(int[] arr, int numToSelect, int sumToSelect, int index) {
29         int count = 0;
30         // 递归出口,如果数全部选择完成,则只需判定sumToSelect是否为零,如果为零,符合条件,返回1,否则返回0
31         if (numToSelect == 0) {
32             return sumToSelect == 0 ? 1 : 0;
33         }
34         /* 
35          * 将问题按选择的第一个数的不同做分解,第一个数可选的范围为[index, arr.length - numToSelect],
36          * 所以就分解成了(arr.length - numToSelect - index + 1)个子问题。可为什么可选下标的右边界是
37          * (arr.length - numToSelect)呢?是因为如果第一个数的下标是(arr.length - numToSelect + 1),
38          * 那么后面只剩(numToSelect - 2)个位置,是不够放下剩余的(numToSelect - 1)个值的。
39          */
40         for (int i = index; i <= arr.length - numToSelect; i++) {
41             if (arr[i] <= sumToSelect) {
42                 /*
43                  * 选择了第一个数arr[i],还需要在剩余数组片段中选择和为(sumToSelect-arr[i])
44                  * 的(numToSelect-1)个数。
45                  * >> 需要递归
46                  */
47                 count += getSchemeNumByNumToSelect(arr, numToSelect - 1, sumToSelect - arr[i], i + 1);
48             }
49         }
50         return count;
51     }
52 }
View Code

2、动态规划dp[][] 

 1 @Test
 2 public void test1() {
 3     // 指定输入 >> 
 4     int[] arr = { 5, 5, 10, 2, 3 };
 5     int sum = 15;
 6     // ================================================
 7     
 8     // 初始化dp二维数组 【dp[i][j]表示用前i个数组成和为j的方案个数】
 9     int rows = arr.length + 1;
10     int cols = sum + 1;
11     int[][] dp = new int[rows][cols];
12     // 初始化dp的第一列,用前i个数组成和为0的方案都只有1种,就是什么都不取;
13     for (int i = 0; i < rows; i++) {
14         dp[i][0] = 1;
15     }
16     // 初始化dp的第一行,用0个元素不能组成1~sum
17     for (int j = 1; j <= sum; j++) {
18         dp[0][j] = 0;
19     }
20     
21     System.out.println("-- 处理前dp:");
22     for (int i = 0; i < rows; i++) {
23         System.out.println((i > 0 ? arr[i - 1] : "附加0") + "	" + Arrays.toString(dp[i]));
24     }
25     System.out.println();
26     
27     // 一行行的计算dp中每个元素的值
28     //System.out.println("附加0 	"+Arrays.toString(dp[0]));
29     for (int i = 1; i < rows; i++) {
30         for (int j = 1; j <= sum; j++) {
31             /*
32              * 用前i个数来组成和为j的组合,所有成功的组合可分下面两种情况:  
33              * 1、 组合中不包含第i个数 ,即只用前i-1个数来组成和为j的组合。
34              * 2、组合中包含第i个数,这要求第i个数不能比和大(前i-1个数要组成和为:j-第i个数)。
35              */
36             dp[i][j] = dp[i - 1][j];
37             if (arr[i-1] <= j) { // 第i个数为arr[i-1]
38                 dp[i][j] += dp[i - 1][j - arr[i-1]]; 
39             }
40         }
41         //System.out.println(arr[i-1]+"	"+Arrays.toString(dp[i]));
42     }
43     
44     System.out.println("-- 处理后dp:");
45     for (int i = 0; i < rows; i++) {
46         System.out.println((i > 0 ? arr[i - 1] : "附加0") + "	" + Arrays.toString(dp[i]));
47     }
48     System.out.println("答案:" + dp[rows-1][sum]);
49 }
50 /* out:
51 -- 处理前dp:
52 附加0    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
53 5    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
54 5    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
55 10    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
56 2    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
57 3    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
58 
59 -- 处理后dp:
60 附加0    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
61 5    [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
62 5    [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
63 10    [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2]
64 2    [1, 0, 1, 0, 0, 2, 0, 2, 0, 0, 2, 0, 2, 0, 0, 2]
65 3    [1, 0, 1, 1, 0, 3, 0, 2, 2, 0, 4, 0, 2, 2, 0, 4]
66 答案:4
67  */
View Code

原文地址:https://www.cnblogs.com/apeway/p/10768907.html