摘自程序员代码面试指南 IT名企算法与数据结构题目最优解 [左程云著][电子工业出版社][2015.09][513页][13859131]
一 问题
给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数求换钱有多少种方法?
举例:
arr=[5,10,25,1],aim=0。组成0元的方法有1种,就是所有面值的货币都不用。所以返回1。
arr=[5,10,25,1],aim=15.
组成15元的方法有6种,分别为3张5元、1张10元+1张5元、1张10元+5张1元、10张1元+1张5元、2张5元+5张1元和15张1元。所以返回6.
arr=[3,5],aim=2。任何方法都无法组成2元。所以返回0.
二 暴力递归
2.1
base case 递归跳出的条件,当走到最后一个数时(index==数组的长度时)
aim=0
2.2代码实现
2.2.1 正推
public static int coins1(int[] arr,int aim)
{
if(arr==null||arr.length==0||aim<0)
{
return 0;
}
return process1(arr,0, aim);
}
public static int process1(int[] arr,int index,int aim)
{
int res=0;
//base case
if(index==arr.length)
{
res=aim==0?1:0;//当走到base case时,如果aim==0了,记为1次
}else
{
for(int i=0;arr[index]*i<=aim;i++)
{
res+=process1(arr,index+1,aim-arr[index]*i);
}
}
return res;
}
2.2.2 反推
public static int coinOther(int[] arr,int aim)
{
if(arr==null||arr.length==0||aim<0)
{
return 0;
}
return processOther(arr,arr.length-1, aim);
}
public static int processOther(int[] arr,int index,int aim)
{
int res=0;
if(index==-1)
{
res=aim==0?1:0;
}else
{
for(int i=0;arr[index]*i<=aim;i++)
{
res+=processOther(arr, index-1, aim-arr[index]*i);
}
}
return res;
}
2.3 时间复杂度分析
最差情况O(aim^N)
三 优化之记忆搜索
3.1 分析
递归存在着多种重复的情况,例如优化方式process中的arr是一直不变化的,所以p(index,aim)表示一个递归过程,可以准备一个map,记录递归过程,有重复的,可以直接拿出来使用
3.2 代码实现
//优化一之记忆搜索
public static int coins(int[] arr,int aim)
{
if(arr==null||arr.length==0||aim<0)
{
return 0;
}
//map[i][j]表示
int[][] map=new int[arr.length+1][aim+1];
return process2(arr, 0, aim, map);
}
public static int process2(int[] arr,int index,int aim,int[][] map)
{
int res=0;
if(index==arr.length)
{
res=aim==0?1:0;
}
else
{
int mapValue=0;
for(int i=0;arr[index]*i<=aim;i++)
{
mapValue=map[index+1][aim-arr[index]*i];
if(mapValue!=0)
{
res+=mapValue==-1?0:mapValue;
}else{
res+=process2(arr, index+1, aim-arr[index]*i, map);
}
}
}
//将此过程记录下来
map[index][aim]=res==0?-1:res;
return res;
}
3.3 时间复杂度分析
四 动态规划
4.1 由暴力递归代码改出动态规划
process1(int[] arr,int index,int aim)中发生变化的只有index和aim,所有创造一个以index和aim为单位的二维数组.代码如下
public static int coins3(int[] arr,int aim)
{
if(arr==null||arr.length==0||aim<0)
{
return 0;
}
//arr.length包括-1,列包括0
int[][] dp=new int[arr.length][aim+1];
//第一列为0
for(int i=0;i=0;k++)
{
num+=dp[i-1][j-arr[i]*k];
}
dp[i][j]=num;
}
}
return dp[arr.length-1][aim];
}
4.2 代码含义
4.3 时间复杂度估计
五,动态规划时间和空间的优化
5.1时间上的优化
注:以上截图有小错误,以此截图为准
public static int coins4(int[] arr,int aim)
{
if(arr==null||arr.length==0||aim<0)
{
return 0;
}
//arr.length包括-1,列包括0
int[][] dp=new int[arr.length][aim+1];
//第一列为0
for(int i=0;i= 0 ? dp[i][j - arr[i]] : 0;
}
}
return dp[arr.length-1][aim];
}