算法习题正整数数组中和为sum的任意个数的组合数
Posted apeway
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法习题正整数数组中和为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 }
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") + "\t" + Arrays.toString(dp[i])); 24 } 25 System.out.println(); 26 27 // 一行行的计算dp中每个元素的值 28 //System.out.println("附加0 \t"+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]+"\t"+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") + "\t" + 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 */
3、动态规划dp[]
View Code
以上是关于算法习题正整数数组中和为sum的任意个数的组合数的主要内容,如果未能解决你的问题,请参考以下文章
给定的数组任意取出三个数(要求全部取到且不能重复)求和,找出和中最小值 Java 说说思路或者算法就
算法系列——和为 K 的子数组(Subarray Sum Equals K)