问题:
给一正整数数组A,一正整数n,求数组A的元素相加能够得到和为n的种数。
例如:数组为[5, 5, 10, 2, 3] ,n 为 15,那么种数就为4,分别为:5 + 10, 5 + 10, 5 + 5 + 2 + 3, 10 + 2 + 3。
分析:
最直接的方法就是枚举,但这样的时间复杂度太高,太暴力了!
其实,这一问题与01背包问题类似。
用f[i][v]表示前i个数中能得到和为v的种数,那么其状态转移方程为:
f[i][v] = f[i-1][v] + f[i-1][v-A[i]]。
使用01背包的优化方法,可以只使用一维数组,即空间复杂度O(n)。
若数组A的元素个数为m,那么时间复杂度为O(n*m)。
代码实现:
int getCount(const vector<int> &v, int n){
int sum = accumulate(v.begin(), v.end(), 0);
if(sum < n) return 0;
vector<int> count(n + 1, 0);
count[0] = 1;
for(int i = 0; i < v.size(); ++i){
for(int sumi = n; sumi >= v[i]; --sumi)//逆序遍历
count[sumi] += count[sumi - v[i]];
}
return count[n];
}
PS:01背包类问题,一般适合背包容量不太大的情况。在这里就是n不能够太大,当n比较大,数组的元素比较少的情况下,搜索+剪枝 算法会更优。
1. 如果数组里面可以为负数呢? 或者说,可以用加法和减法两种运算呢?印象中,这种情况是微软的一道笔试题。
2. 如果要求的是不能重复的种数呢?例如:数组为[5, 5, 10, 2, 3] ,n 为 15。这时,两个5+10=15只能算作一种,这样总的种数为3。