待字闺中之正数数组内和为指定数字的总数
2014-08-05 15:07
141 查看
求正数数组内和为指定数字的合并总数
比如[5, 5, 10, 2, 3] 合并值为 15 : 有4种 : (5 + 10, 5 + 10, 5 + 5 + 2 + 3, 10 + 2 + 3)
分析:有的时候,一个题目不能够立刻想到比较优化的想法,就可以先找到一个解决方案,然后根据方案的不足进行优化。而且这个时候,逆转一下思路,便会柳暗花明。由递归到动态规划,不就是如此么?
我们设定f(index,sum)表示数组从index开始到结束组成的sum的总数。那么,f(index, sum)可以表示为什么呢? 我们这个题目,就是要最终求得f(0, 15),从头开始分析,最终组成的和为15的可能组合中,可能包含第0个元素,也可能不包含, 原始数组为A:
当包含第0个元素时,剩下的表示为f(1, 15-A[0])
不包含第0个元素时,剩下的表示为f(1, 15)
则,f(0, 15) = f(1, 15) + f(1, 15 - A[0])。依次递归。递归的终止条件是什么呢?对于f(index,sum):
当和小于等于0的时候,f(index,sum) = 0
当和小于sum的时候, f(index, sum) = f(index + 1, num);
当和等于sum的时候,f(index, sum) = 1 + f(index + 1, sum);
但是,上面的条件,并没有使用题目中,数组全是正数,也就是存在负数也可以。如果仅仅是正数,后两个改为:
当和小于sum的时候, f(index, sum) = 0;
当和等于sum的时候,f(index, sum) = 1;
有一个条件,我们没有使用,也意味着提升的空间。
可是,上面的方案,时间复杂度是指数级。怎么做一些改进呢?一般在对一个算法进行优化的时候,有哪些思路呢?尤其是这种时间很恐怖的?我想很多同学都有这个经验,就是空间换时间。
大家可以想象动态规划的思想,大家看如下的状态转移方程:
dp
[m]=dp[n-1][m]+dp[n-1][m-num[n-1]]
dp
[m]表示前n个元素组成和为m的情况数。初始化dp[0][0]=1,其他为0。写出状态转移方程,大家也就明白了,为何要求全是正数了吧,直白一些,数组的索引,怎么可能为负呢?在计算的过程中,将和的情况保存下来,用空间换时间,整个算法的时间复杂度为O(n*m),不再是指数级。
具体代码如下:
比如[5, 5, 10, 2, 3] 合并值为 15 : 有4种 : (5 + 10, 5 + 10, 5 + 5 + 2 + 3, 10 + 2 + 3)
分析:有的时候,一个题目不能够立刻想到比较优化的想法,就可以先找到一个解决方案,然后根据方案的不足进行优化。而且这个时候,逆转一下思路,便会柳暗花明。由递归到动态规划,不就是如此么?
我们设定f(index,sum)表示数组从index开始到结束组成的sum的总数。那么,f(index, sum)可以表示为什么呢? 我们这个题目,就是要最终求得f(0, 15),从头开始分析,最终组成的和为15的可能组合中,可能包含第0个元素,也可能不包含, 原始数组为A:
当包含第0个元素时,剩下的表示为f(1, 15-A[0])
不包含第0个元素时,剩下的表示为f(1, 15)
则,f(0, 15) = f(1, 15) + f(1, 15 - A[0])。依次递归。递归的终止条件是什么呢?对于f(index,sum):
当和小于等于0的时候,f(index,sum) = 0
当和小于sum的时候, f(index, sum) = f(index + 1, num);
当和等于sum的时候,f(index, sum) = 1 + f(index + 1, sum);
但是,上面的条件,并没有使用题目中,数组全是正数,也就是存在负数也可以。如果仅仅是正数,后两个改为:
当和小于sum的时候, f(index, sum) = 0;
当和等于sum的时候,f(index, sum) = 1;
有一个条件,我们没有使用,也意味着提升的空间。
可是,上面的方案,时间复杂度是指数级。怎么做一些改进呢?一般在对一个算法进行优化的时候,有哪些思路呢?尤其是这种时间很恐怖的?我想很多同学都有这个经验,就是空间换时间。
大家可以想象动态规划的思想,大家看如下的状态转移方程:
dp
[m]=dp[n-1][m]+dp[n-1][m-num[n-1]]
dp
[m]表示前n个元素组成和为m的情况数。初始化dp[0][0]=1,其他为0。写出状态转移方程,大家也就明白了,为何要求全是正数了吧,直白一些,数组的索引,怎么可能为负呢?在计算的过程中,将和的情况保存下来,用空间换时间,整个算法的时间复杂度为O(n*m),不再是指数级。
具体代码如下:
int totalCountRecusive(vector<int>& data,int index,int sum) { int length = data.size(); if(sum == 0) return 1;//找到一个结果 if(index == length)return 0; return totalCountRecusive(data,index+1,sum-data[index]) + totalCountRecusive(data,index+1,sum);//递归 } int totalCountRecusive(vector<int>& data,int sum)//递归方法 { return totalCountRecusive(data,0,sum); } int totalCountDp(vector<int>& data,int sum)//动态规划方法 { int length = data.size(); if(length <= 0)return 0; vector<vector<int> > dp(length+1); int i,j; for(i = 0;i <= length;i++) { vector<int> tmp(sum+1,0);//初始化 dp[i] = tmp; } for(i = 0;i <= length;i++)dp[i][0] = 1;//初始化 for(i = length-1;i >= 0;i--) { for(j = sum;j > 0;j --) { dp[i][j] = dp[i+1][j]; if(j - data[i] >= 0)dp[i][j] += dp[i+1][j-data[i]];//分开,防止下标为负数 } } return dp[0][sum]; }
相关文章推荐
- c 语言去除数组中指定的数字
- 找数组中和为指定数的数字
- leetcode-1 Two Sum 找到数组中两数字和为指定和
- 找出数组中超出总数1/4的数字
- 在数组中找出四个数字的和等于指定数字(4Sum)
- 数据结构与算法读书笔记4----C# 查找数组中指定数字,最小值,最大值。
- 数组求和为指定数字
- Two Sum(找出数组中两个和等于指定数字的元素)
- 剑指off-找到数组中个数大于总数一半的数字
- 【leetcode】删除数组中指定要求的重复的数字
- 和为n连续正数序列 & 排序数组中和为给定值的两个数字
- leetcode-1 Two Sum 找到数组中两数字和为指定和
- 【c语言】使用NULL和指针来寻找数组中是否存在指定的数字
- ajax提交指定数字键名数组的方法
- 指定数字在数组中第一次出现的位置
- 将整型数字转化成指定位数的等值的字符数组
- 将一组整数数组中的数字按负数在前,零在中间,正数在末尾摆放。
- 如何在有序数组中给出指定数字出现的次数
- 数字字符串转为指定数组
- 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007