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

 public class Test {

     @org.junit.Test
public void test() {
System.out.println("方案数:" + getAllSchemeNum(new int[]{ 5, 5, 5, 2, 3 }, 15));
} // out : 方案数:4 /**
* 从数组中选择和为sum的任意个数的组合数
*/
public static int getAllSchemeNum(int[] arr, int sum) {
int count = 0;
// 将 选择一个数的组合数、选择两个数的组合数、...选择n个数的组合数 相加
for (int numToSelect = 1; numToSelect <= arr.length; numToSelect++) {
count += getSchemeNumByNumToSelect(arr, numToSelect, sum, 0);
}
return count;
} /**
* 求【从数组的[arr[index], arr[length-1]]片段中获取和为sumToSelect的numToSelect个数】的方案数
* @param arr 数组
* @param numToSelect 还需要选择的数的个数
* @param sumToSelect 还需要选择数之和
* @param index 可选的范围的左边界
* @return
*/
public static int getSchemeNumByNumToSelect(int[] arr, int numToSelect, int sumToSelect, int index) {
int count = 0;
// 递归出口,如果数全部选择完成,则只需判定sumToSelect是否为零,如果为零,符合条件,返回1,否则返回0
if (numToSelect == 0) {
return sumToSelect == 0 ? 1 : 0;
}
/*
* 将问题按选择的第一个数的不同做分解,第一个数可选的范围为[index, arr.length - numToSelect],
* 所以就分解成了(arr.length - numToSelect - index + 1)个子问题。可为什么可选下标的右边界是
* (arr.length - numToSelect)呢?是因为如果第一个数的下标是(arr.length - numToSelect + 1),
* 那么后面只剩(numToSelect - 2)个位置,是不够放下剩余的(numToSelect - 1)个值的。
*/
for (int i = index; i <= arr.length - numToSelect; i++) {
if (arr[i] <= sumToSelect) {
/*
* 选择了第一个数arr[i],还需要在剩余数组片段中选择和为(sumToSelect-arr[i])
* 的(numToSelect-1)个数。
* >> 需要递归
*/
count += getSchemeNumByNumToSelect(arr, numToSelect - 1, sumToSelect - arr[i], i + 1);
}
}
return count;
}
}

2、动态规划dp[][]

 @Test
public void test1() {
// 指定输入 >>
int[] arr = { 5, 5, 10, 2, 3 };
int sum = 15;
// ================================================ // 初始化dp二维数组 【dp[i][j]表示用前i个数组成和为j的方案个数】
int rows = arr.length + 1;
int cols = sum + 1;
int[][] dp = new int[rows][cols];
// 初始化dp的第一列,用前i个数组成和为0的方案都只有1种,就是什么都不取;
for (int i = 0; i < rows; i++) {
dp[i][0] = 1;
}
// 初始化dp的第一行,用0个元素不能组成1~sum
for (int j = 1; j <= sum; j++) {
dp[0][j] = 0;
} System.out.println("-- 处理前dp:");
for (int i = 0; i < rows; i++) {
System.out.println((i > 0 ? arr[i - 1] : "附加0") + "\t" + Arrays.toString(dp[i]));
}
System.out.println(); // 一行行的计算dp中每个元素的值
//System.out.println("附加0 \t"+Arrays.toString(dp[0]));
for (int i = 1; i < rows; i++) {
for (int j = 1; j <= sum; j++) {
/*
* 用前i个数来组成和为j的组合,所有成功的组合可分下面两种情况:
* 1、 组合中不包含第i个数 ,即只用前i-1个数来组成和为j的组合。
* 2、组合中包含第i个数,这要求第i个数不能比和大(前i-1个数要组成和为:j-第i个数)。
*/
dp[i][j] = dp[i - 1][j];
if (arr[i-1] <= j) { // 第i个数为arr[i-1]
dp[i][j] += dp[i - 1][j - arr[i-1]];
}
}
//System.out.println(arr[i-1]+"\t"+Arrays.toString(dp[i]));
} System.out.println("-- 处理后dp:");
for (int i = 0; i < rows; i++) {
System.out.println((i > 0 ? arr[i - 1] : "附加0") + "\t" + Arrays.toString(dp[i]));
}
System.out.println("答案:" + dp[rows-1][sum]);
}
/* out:
-- 处理前dp:
附加0 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
5 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
5 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
10 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
2 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
3 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -- 处理后dp:
附加0 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
5 [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
5 [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
10 [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2]
2 [1, 0, 1, 0, 0, 2, 0, 2, 0, 0, 2, 0, 2, 0, 0, 2]
3 [1, 0, 1, 1, 0, 3, 0, 2, 2, 0, 4, 0, 2, 2, 0, 4]
答案:4
*/

【算法习题】正整数数组中和为sum的任意个数的组合数的更多相关文章

  1. 【算法习题】数组中任意2个(3个)数的和为sum的组合

    题1.给定一个int数组,一个数sum,求数组中和为sum的任意2个数的组合 @Test public void test_find2() { int[] arr = { -1, 0, 2, 3, 4 ...

  2. (回溯法)数组中和为S的N个数

    Given a list of numbers, find the number of tuples of size N that add to S. for example in the list ...

  3. Two sum(给定一个无重复数组和目标值,查找数组中和为目标值的两个数,并输出其下标)

    示例: nums = [1,2,5,7] target = [6] return [0,2] Python解决方案1: def twoSum(nums, target): ""&q ...

  4. 18. 4Sum -- 找到数组中和为target的4个数

    Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = tar ...

  5. 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数 例如给定nums = [2,7,11,15],target = 9

    python解决方案 nums = [1,2,3,4,5,6] #假如这是给定的数组 target = 9 #假如这是给定的目标值 num_list = [] #用来装结果的容器 def run(nu ...

  6. 1001 数组中和等于K的数对 1002 数塔取数问题 1003 阶乘后面0的数量 1004 n^n的末位数字 1009 数字1的数量

    1001 数组中和等于K的数对 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 给出一个整数K和一个无序数组A,A的元素为N个互不相同的整数,找出数组A中所有和等于K ...

  7. c++刷题(12/100)无序数组中和为定值的最长子数组

    题目一: 最短无序连续子数组 给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序. 你找到的子数组应是最短的,请输出它的长度. 示例 1: 输入: ...

  8. [经典算法题]寻找数组中第K大的数的方法总结

    [经典算法题]寻找数组中第K大的数的方法总结 责任编辑:admin 日期:2012-11-26   字体:[大 中 小] 打印复制链接我要评论   今天看算法分析是,看到一个这样的问题,就是在一堆数据 ...

  9. 【bzoj3289】Mato的文件管理 离散化+莫队算法+树状数组

    原文地址:http://www.cnblogs.com/GXZlegend/p/6805224.html 题目描述 Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份 ...

随机推荐

  1. C语言的AT指令

    今天跟人聊嵌入式,对面是某国际硬盘生产商的嵌入式软件工程师,问了我很简单的问题,如何快速将一个变量赋给某个特定的地址. 按我们思路就是unsigned *a = address1:  *a = add ...

  2. jQuery-3.事件篇---事件对象的使用

    jQuery事件对象的作用 事件中的Event对象容易被初学者忽略掉,可能大多时候初学者不知道怎么去用它,但有些时候它还是非常有用的 一个标准的"click"点击事件 $(elem ...

  3. AI之旅(4):初识逻辑回归

    前置知识   求导 知识地图   逻辑回归是用于分类的算法,最小的分类问题是二元分类.猫与狗,好与坏,正常与异常.掌握逻辑回归的重点,是理解S型函数在算法中所发挥的作用,以及相关推导过程. 从一个例子 ...

  4. SQL Server中的连接查询(内连接、外连接、交叉连接)

    在数据库查询中,经常会用到两个有关联的表进行查询,需要把两个表中的数据按照某些条件查出来,这时就可以使用连接查询 连接查询分为三种:内连接.外连接和交叉连接 1. 内连接 内连接inner join ...

  5. hadoop MapReduce

    简单介绍 官方给出的介绍是hadoop MR是一个用于轻松编写以一种可靠的.容错的方式在商业化硬件上的大型集群上并行处理大量数据的应用程序的软件框架. MR任务通常会先把输入的数据集切分成独立的块(可 ...

  6. Linux 6.8 源码安装MySQL8.0

    搭建环境说明: 系统版本:Red Hat Enterprise Linux Server release 6.8 (Santiago) 内核版本:Linux 2.6.32-642.el6.x86_64 ...

  7. Mac os x 系统的发展史

    ·Mac OS 9:发布时间:1999年 于1999年发布的Mac OS 9操作系统(图片来自互联网) 在OS X之前,1999年发布的Mac OS 9看起来就是一个普通的桌面操作系统.并且现在已经被 ...

  8. D类IP地址和组播传输

    在224.0.0.0-239.255.255.255范围内的地址称为D类IP组播地址.其中,224.0.0.0-224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配 ...

  9. 20164301 Exp4 恶意代码分析

    Exp4 恶意代码分析 实验目标 1.是监控你自己系统的运行状态,看有没有可疑的程序在运行.  2.是分析一个恶意软件,就分析Exp2或Exp3中生成后门软件:分析工具尽量使用原生指令或sysinte ...

  10. admin-2

    Linux是一种服务器操作系统 操作系统:一堆软件的集合,可以让计算机硬件正常工作 • UNIX诞生,1970-1-1(Linux系统时间的起点) • Linux之父,Linus Torwalds 内 ...