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 }