一、题目
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。
题目数据保证答案符合 32 位整数范围。
输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。
二、解法
思路:动态规划
该问题本质上是一个背包问题。
2.1 背包问题
0-1的背包问题:
有n个物品,它们具有两个属性:重量
W
i
W_i
Wi,价值
V
i
V_i
Vi(
1
≤
i
≤
n
1\le i\le n
1≤i≤n),给定一个承重容量为
m
m
m的背包,问怎么承装物品,使得背包装下的物品总价值最大(每种物品只有一个)。
思路是用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示在前i个物品中选择,背包容量为j时的最大价值。
- 则转移方程为: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , V i + d p [ i − 1 ] [ j − W i ] ) ( 需 要 考 虑 是 否 j ≥ W i ) dp[i][j]=max(dp[i-1][j],V_i+dp[i-1][j-W_i])(需要考虑是否j\ge W_i) dp[i][j]=max(dp[i−1][j],Vi+dp[i−1][j−Wi])(需要考虑是否j≥Wi)
- 最终求解的是 d p [ n ] [ m ] dp[n][m] dp[n][m]
完全背包问题:
有n种物品(都是无限数量的),它们各自的重量为
W
i
W_i
Wi,价值为
V
i
V_i
Vi,给定背包承重容量为
m
m
m,求怎么使背包承装物品的总价值最大。
思路:dp数组的含义和01背包问题相同,但状态转移方程不同: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , V i + d p [ i ] [ j − W i ] ) dp[i][j]=max(dp[i-1][j],V_i+dp[i][j-W_i]) dp[i][j]=max(dp[i−1][j],Vi+dp[i][j−Wi]),注意到后面不是 i − 1 i-1 i−1而是 i i i
2.2 组合问题
本题求解组合个数,看起来跟完全背包问题差不太多,但是本题要求是组合,即顺序也会影响,比如 1 , 2 , 3 1,2,3 1,2,3和{2,1,3}是不同的组合。
用
d
p
[
i
]
dp[i]
dp[i]表示总和为
i
i
i时的组合个数,最终求解
d
p
[
t
a
r
g
e
t
]
dp[target]
dp[target],初始条件:
d
p
[
0
]
=
1
dp[0]=1
dp[0]=1
状态转移方程:
d
p
[
i
]
+
=
d
p
[
i
−
n
u
m
s
[
j
]
]
(
j
≤
i
,
i
≥
n
u
m
s
[
j
]
)
dp[i]+=dp[i-nums[j]](j\le i,i\ge nums[j] )
dp[i]+=dp[i−nums[j]](j≤i,i≥nums[j]),含义:总和为
i
i
i的组合个数,等于总和为
i
−
n
u
m
s
[
j
]
i-nums[j]
i−nums[j]的组合个数的累加
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
int n=nums.size();
vector<unsigned int> dp(target+1,0);
dp[0]=1;
for(int i=1;i<=target;++i){
for(int j=0;j<n;++j){
if(i>=nums[j]){
dp[i]+=dp[i-nums[j]];
}
}
}
return dp[target];
}
};