Given an integer array nums
with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target
.
Notice
The different sequences are counted as different combinations.
Given nums = [1, 2, 4]
, target = 4
The possible combination ways are:
[1, 1, 1, 1]
[1, 1, 2]
[1, 2, 1]
[2, 1, 1]
[2, 2]
[4]
return 6
class Solution {
public:
/**
* @param nums an integer array and all positive numbers, no duplicates
* @param target an integer
* @return an integer
*/
int backPackVI(vector<int>& nums, int target) {
// Write your code here
const int len = nums.size();
vector<vector<int>> vec_res;
int res = 0;
sort(nums.begin(), nums.end());
vector<int> map;
for (int i = 0; i < len; i++) {
map.push_back(nums[i]);
map.push_back(0);
}
queue<pair< pair<vector<int>, int>, int >> que;
for (int i = 0; i < len; i++) {
map[i * 2 + 1]++;
pair<vector<int>, int> p_t = make_pair(map, nums[i]);
que.push(make_pair(p_t, i));
map[i * 2 + 1]--;
}
while (!que.empty()) {
vector<int> vec= que.front().first.first;
vector<int> temp;
int sum = que.front().first.second;
int sum_temp;
int ind = que.front().second;
que.pop();
if (sum == target) {
vec_res.push_back(vec);
continue;
}
for (int i = ind; i < len; i++) {
temp = vec;
sum_temp = sum;
temp[i * 2 + 1]++;
sum_temp+=nums[i];
if (sum_temp > target) {
break;
}
pair<vector<int>, int> p_t = make_pair(temp, sum_temp);
que.push(make_pair(p_t, i));
}
}
// for (int i = 0; i < vec_res.size(); i++) {
// for (int j = 0; j < vec_res[i].size(); j++)
// cout<<vec_res[i][j]<<" ";
// cout<<endl;
// }
for (int i = 0; i < vec_res.size(); i++) {
//cout<<get_num(vec_res[i])<<endl;
res+=get_num(vec_res[i]);
}
return res;
}
long long get_num(vector<int>& vec) {
long long total = 0;
for (int i = 1; i < vec.size(); i+=2) {
total+=vec[i];
}
total = get_mul(total);
for (int i = 1; i < vec.size(); i+=2) {
total/=get_mul(vec[i]);
}
return total;
}
long long get_mul(int n) {
long long res = 1;
if ( n == 0)
return 1;
for (int i = n; i >=1; i--)
res*=i;
return res;
}
};
虽然小数据集能通过,但是有两个问题:第一是大数据集会溢出, 第二是耗时比较长。
所以考虑更简单的解法;
是否要把所有的可能都要列出来呢?
其实不是,题目求的是个数,而不是全部都列出来。
突然想到dp,对于某个数i,只要把它拆成两部分(i = m + n, 0 <= m <= i),那么每部分分别计算dp[m]*dp[n], 然后加起来,就可以得到总的解的个数。
然而重复的问题使得这样的解法有问题;
再进一步思考对于种组合i=m+n,如何去重呢?比如 (1,2,3)+ (4, 5,6)与 (1,2,3,4)+ (5,6),
其实看上面两种组合都与(1)+(2,3,4,5,6)重复,(1,2,3,4,5,6)只需计算一次, 便可任意组合成不同的拆分方案,
所以,我们只需要这样计算:第一部分是给出数组中的某一个值, 第二部分是target减去这个数的值,累加起来便可,
比如[1,2,3], target = 4, 只要计算
dp[3] ( 1 + 3);
dp[2] ( 2 + 2);
dp[0] ( 4 + 0);
然后累加便可;
实现非常简洁:
class Solution {
public:
/**
* @param nums an integer array and all positive numbers, no duplicates
* @param target an integer
* @return an integer
*/
int backPackVI(vector<int>& nums, int target) {
// Write your code here
const int len = target + 1;
int dp[len];
sort(nums.begin(), nums.end());
memset(dp, 0, sizeof(dp));
dp[0] = 1;
for (int i = 1; i <= target; i++) {
for (int j = 0; j < nums.size(); j++) {
if (i < nums[j])
break;
dp[i] += (dp[i - nums[j]]);
}
}
return dp[target];
}
};