每日一题之动归-换钱的最少次数(二)

题目:上一道题是给定一个钱的数组,可以使用任意张数。接下来改变一下题目:给定数组arr,arr中所有的值都是正数。每个值仅代表一张钱的面值,再给定一个整数aim代表要找的钱数,求组成aim的最少货币数。

例子:
arr=[5,2,3],aim=20。
这里5+2+3=10最大才是10,所以不能组成,返回-1。
arr=[5,2,5,3],aim=10。
这里有两种可能一种是5+5 ,还有一种是5+2+3,我们选择两次的,返回2。
arr=[5,2,5,3 ],aim=15。
这里返回4。
arr=[5,2,5,3],aim=0。
不用任何货币就可以组成0元,返回0。
分析:这里和我们的上一道题很类似。
我们一样使用经典的动态规划来做,时间复杂度在O(N×aim)。
如果我们arr的长度为N,目标钱值为aim。我们生成一个大小为N X aim的二维数组dp。dp[i][j]的含义是在只使用arr[0..i]货币的情况下,组成j所需要的最小张数。所以同样的计算方法如下:
1.dp[0..N-1][0]的值表示找的钱数为0的时候需要的最少张数,钱数为0时,完全不需要任何货币所以全部设置为0. 我们这里使用的是java初始化,所以不需要特意的去把数组初始化为0。
2.dp[0][0..aim]表示只能使用arr[0]货币的情况下找某个签署的最小张书。比如,arr[0]=2,那么能找开的钱数只能为2所以令dp[0][2]=1
其他位置都是找不开的,其余一律设为32位整数最大值,我们把这个值记为max。
3.基本步骤哦做完后,如果j-arr[i]<0,则dp[i][j]=dp[i-1][j]。
如果大于0则dp[i][j]=min{dp[i-1][j],dp[i-1][j-arr[i]]+1}。代码如下:

public int min(int[] arr,int aim){
  if(arr==null||arr.length==0||aim<0){
      return -1;//正确性判断
  }
  int n = arr.length;
  int max = Integer.MAX_VALUE;
  int[][] dp=new int[n][aim+1];
  for(j=1;j<=aim;j++){
    dp[0][j]=max;//对于上面分析的第二步的初始化
  }
  if(arr[0]<=aim){
   dp[0][arr[0]]=1;//对该部分设置为1。
  }
  int tmp=0;//用来记录当前左上角
  for(int i=1;i<n;i++){
      for(int j=1;j<=aim;j++){
      tmp=max;
        if(j-arr[i]>=0&&dp[i-1][j-arr[i]]!=max){
                        tmp=dp[i-1][j-arr[i]]+1;
        }
        dp[i][j]=Math.min(tmp,dp[i-1][j]);
       }
  }
  return dp[n-1][aim]!=max?dp[n-1][aim]:-1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值