题目:给定一个数组Arr,每一个元素表示一种硬币 ,数量无限。现在需要这些种硬币组成我们的目标是aim,问有多少中组合的方法;
Arr = {3,2,1,5,7,3},aim = 20;
-
- 递归解法
寻找可变参数:idx表示从数组中的第idx以后的元素中找到组成rest的组合数;rest的初始值为aim;这类递归问题可以想成是一颗树;
f(idx,rest)表示数组Arr的第idx以后的元素集合,从这个集合中选取硬币组成rest的组合数;
- 递归解法
-
code
//表示从数组种的idx位置开始以后种类的货币组成rest有多少种方法 int get_N1(vector& Arr, int idx, int rest) { if (idx == Arr.size()) return rest == 0 ? 1 : 0; int res = 0; for (int k = 0; k * Arr[idx] <= rest; k++) { res += get_N1(Arr, idx + 1, rest - Arr[idx] * k); } return res; }
for循环的意义就是一个节点从所有的孩子拿信息,只不过这里的孩子说是根据给定的Arr已经aim才能确定,所以写成这种形式;
举例说明:Arr=[3,4,2,34,7,23],aim=20
面值3的硬币使用0次,get_N1(Arr,1,aim)
面值3的硬币使用1次,get_N1(Arr,1,aim-31)
面值3的硬币使用2次,get_N1(Arr,1,aim-32)
面值3的硬币使用6次,get_N1(Arr,1,aim-3*6)
使用7次,rest就小于零了;并且一直使用面值3,不能达到aim;
-
- 严格表结构
/严格表结构 int get_N2(vector& Arr, int idx, int rest) { vector > dp(Arr.size() + 1, vector (rest+1, 0)); dp[Arr.size()][0] = 1; for (int i = Arr.size() - 1; i >= 0;i--) { for (int j = 0; j <= rest; j++) { int res = 0; for (int k = 0; k * Arr[idx] <= rest; k++) { res += dp[i + 1][rest - Arr[i] * k]; } dp[i][j] += res; } } return dp[idx][rest]; }
-
- 优化版本
复用dp数组的信息
- 优化版本
/严格表结构 int get_N2(vector& Arr, int idx, int rest) { vector > dp(Arr.size() + 1, vector (rest+1, 0)); dp[Arr.size()][0] = 1; for (int i = Arr.size() - 1; i >= 0;i--) { for (int j = 0; j <= rest; j++) { dp[i][j] = dp[i + 1][j]; if (j - Arr[i] >= 0) { if (j - Arr[i] >= 0) dp[i][j] += dp[i][j - Arr[i]]; } } } return dp[idx][rest]; }
- 心得
动态规划的题目重要是找到尝试的思路,也就是递归版本;可以i尝试左右边界,取值范围等;找到可变参数是解题的关键,可变参数越少越好,可变参数最好是一个值,不要使用数组类型或者其他类型的可变参数,最后确定可变参数的范围



