题目:
定义一种新货币,有n(n<=50)种不同的币值,其中币值为 value(value<=50) 的有 w(w<=20) 个。现在你有 x(x<=100) 元,但是你想将 x 元换成若干零钱,请问有多少种换钱的方案?
方法一:动态规划
这是背包问题的变形,我们可以定义dp[i][j]为用前i种硬币凑出金额j的方案数,
初始条件为,因为前i种硬币凑出金额0的方案数为1
第i种硬币数量有a[i-1][1]个,因此枚举第i种硬币的数量,则前i种硬币凑出金额j的方案数等于前种硬币凑出金额j不加入k个第i种硬币的方案数的累加和,因此状态转移方程为
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) { // write code //用前i种币值凑出金额j的方案数为dp[i][j] int[][]dp=new int[n+1][x+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++){ for(int k=0;k<=a[i-1][1];k++){//枚举第i种硬币的个数 if(j>=k*a[i-1][0])//如果金额数量大于等于k个第i种硬币则可以将k个第i种硬币放入 dp[i][j]+=dp[i-1][j-k*a[i-1][0]]; else break; } } } return dp[n][x]; } }
复杂度:
时间复杂度: ,三层循环,最内层循环k循环次数不超过
空间复杂度:,dp数组的大小不超过
方法二:记忆化搜索
将动态规划改写为记忆化搜索,用记忆数组记录已经计算过的方案数,避免大量的重复运算
递归公式为:
import java.util.*; public class Solution { /** * * @param n int整型 :牛币值种类数 * @param x int整型 :牛妹拥有的钱数 * @param a int整型二维数组 :第二个vector中的第一列表示币值,第二列表示牛牛拥有币值的个数 * @return int整型 */ int[][]memory; public int solve (int n, int x, int[][] a) { // write code //用i种币值凑出金额j的方案数为dp[i][j] memory=new int[n+1][x+1]; for(int i=0;i<a.length;i++)memory[i][0]=1;//初始化记忆数组,i种硬币凑出金额0的方案数都为1 return dp(n,x,a); } int dp(int i,int j,int[][]a){ //递归终止条件 if(i>=0&&j==0)return 1; if(i==0&&j>0)return 0; //已经计算过值直接返回 if(memory[i][j]!=0)return memory[i][j]; //前i种硬币凑成金额j的方案数由前i-1种硬币凑出金额j-k个第i种硬币的方案数递推而来 for(int k=0;k<=a[i-1][1];k++){ if(j>=k*a[i-1][0]) memory[i][j]+=dp(i-1,j-k*a[i-1][0],a); } return memory[i][j]; } }
复杂度:
时间复杂度:递归次数不超过,每次递归时间复杂度为,因此时间复杂度为
空间复杂度:,记忆数组和递归运行时栈大小不超过