• 【算法习题】正整数数组中和为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

  • 相关阅读:
    Message Flood SDUT 1500
    SDUT1482——二元多项式
    SDUT—2057 模拟题
    Eight(bfs+全排列的哈希函数)
    Pick-up sticks(判断两条线段是否相交)
    poj 1265 Area(pick定理)
    poj 2503 Babelfish(字典树哈希)
    poj 3007 Organize Your Train part II(静态字典树哈希)
    Surprising Strings(map类)
    Strange Way to Express Integers (一般模线性方程组)
  • 原文地址:https://www.cnblogs.com/apeway/p/10768907.html
Copyright © 2020-2023  润新知