题意整理
- 有n种不同的货币,给定每种货币的面值和个数。
- 现在要将x元兑换为这些货币的组合,求总共有多少种方案。
方法一(动态规划)
1.解题思路
这是一个典型的背包问题,x元是背包容量,根据面值以及个数限制逐个推导出每种金额x对应的方案数。
- 状态定义:表示i种币值下,j元的金额有多少换钱方案。
- 状态初始化:当金额为0元时,所有的状态只有1种方案。
- 状态转移:每增加一种面额的货币,新增对应的方案数。比如已经计算完货币面额为1元的所有状态,当前货币面额为2元,金额为4元时,如果2元货币为0个,则直接是之前状态为4的情形;如果2元货币为1个,则直接是之前状态为2的情形;如果2元货币为2个,则直接是之前状态为0的情形;将这几种情况累加即是当前金额为4的最终状态。状态方程是。
动图展示:
2.代码实现
import java.util.*; public class Solution { /** * * @param n int整型 :牛币值种类数 * @param x int整型 :牛妹拥有的钱数 * @param a int整型二维数组 :第二个vector中的第一列表示币值,第二列表示牛牛拥有币值的个数 * @return int整型 */ public int solve (int n, int x, int[][] a) { //定义dp数组,dp[i][j]表示i种币值下,j元的金额有多少换钱方案 int[][] dp=new int[n+1][x+1]; //只有0元时,统一1种方案 for(int i=0;i<=n;i++){ dp[i][0]=1; } //遍历所有货币 for(int i=1;i<=n;i++){ //持有多少钱 for(int j=1;j<=x;j++){ //使用多少张a[i-1][0]面值的货币 for(int k=0;k<=a[i-1][1];k++){ if(j-k*a[i-1][0]>=0){ dp[i][j]+=dp[i-1][j-k*a[i-1][0]]; } } } } return dp[n][x]; } }
3.复杂度分析
- 时间复杂度:假设每种硬币数量最多为k个,循环体总共三层,执行次数最多为,所以最终的时间复杂度是。
- 空间复杂度:需要额外大小为的dp数组,所以空间复杂度是。
方法二(状态压缩)
1.解题思路
由于每种状态只与之前对应面值的状态有关,可以考虑只开一维空间。计算的时候需要从后往前推,防止重复计算。
2.代码实现
import java.util.*; public class Solution { /** * * @param n int整型 :牛币值种类数 * @param x int整型 :牛妹拥有的钱数 * @param a int整型二维数组 :第二个vector中的第一列表示币值,第二列表示牛牛拥有币值的个数 * @return int整型 */ public int solve (int n, int x, int[][] a) { //定义dp数组,dp[j]表示j元的金额有多少换钱方案 int[] dp=new int[x+1]; //初始化 dp[0]=1; for(int i=0;i<n;i++){ //从后往前推状态 for(int j=x;j>0;j--){ //使用多少张a[i-1][0]面值的货币 for(int k=1;k<=a[i][1];k++){ if(j-k*a[i][0]>=0){ dp[j]+=dp[j-k*a[i][0]]; } } } } return dp[x]; } }
3.复杂度分析
- 时间复杂度:假设每种硬币数量最多为k个,循环体总共三层,执行次数最多为,所以最终的时间复杂度是。
- 空间复杂度:需要额外大小为x+1的dp数组,所以空间复杂度是。