换钱的最少货币数

【题目】

给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求组成aim的最少货币数。

【举例】

arr=[5,2,3],aim=20
4张5块可以组成20,其他的方案都要使用更多张的货币,所以返回4.
arr=[5,2,3],aim=0
不用任何货币就可以组成0元,所以返回0.
arr=[3,5],aim=2
根本无法组成2元,钱不能找开的情况下默认返回-1.

【代码】

    public static void main(String[] args) {
        int[] m={5,2,3};
        //方法1
        System.out.println(minCoins1(m,20));//4
        System.out.println(minCoins1(m,0));//0
        System.out.println(minCoins1(new int[]{5,3},2));//-1
        //方法2
        System.out.println(minCoins2(m,20));//4
        System.out.println(minCoins2(m,0));//0
        System.out.println(minCoins2(new int[]{5,3},2));//-1
    } 

    //换钱的最小货币数
    //方法1,经典动态规划,复杂度都为O(N×aim)
    public static int minCoins1(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];
        //dp[i][j]的意义:任意使用arr[0...n]组成j所需的最小张数
        //赋值dp的第一行,表示只能使用arr[0]的情况下,找某个钱数的最小张数
        //如arr[0]=2,能找开2,4,6,8... 所以dp[0][2]=1,dp[0][4]=2...其他为max
        for(int j=1;j<=aim;j++){
            dp[0][j]=max;
            if(j-arr[0]>=0 && dp[0][j-arr[0]]!=max){
                //aim>arr[0],且j是arr[0]的倍数
                dp[0][j]=dp[0][j-arr[0]]+1;
            }
        }
        int left=0;
        for(int i=1;i<n;i++){
            for(int j=1;j<=aim;j++){
                left=max;
                if(j-arr[i]>=0&&dp[i][j-arr[i]]!=max){
                    left=dp[i][j-arr[i]]+1;
                }
                dp[i][j]=Math.min(left, dp[i-1][j]);
                //一般情况,dp[i][j]=min{ dp[i-1][j], dp[i][j-arr[i]]+1 }
            }
        }
        return dp[n-1][aim]!=max?dp[n-1][aim]:-1;   
    }

    //方法2,空间压缩,时间复杂度为O(N×aim),空间复杂度为O(aim)
    public static int minCoins2(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[aim+1];
        for(int j=1;j<=aim;j++){
            dp[j]=max;
            if(j-arr[0]>=0 && dp[j-arr[0]]!=max){
                dp[j]=dp[j-arr[0]]+1;
            }
        }
        int left=0;
        for(int i=1;i<n;i++){
            for(int j=1;j<=aim;j++){
                left=max;
                if(j-arr[i]>=0 && dp[j-arr[i]]!=max){
                    left=dp[j-arr[i]]+1;
                }
                dp[j]=Math.min(dp[j], left);
            }
        }
        return dp[aim]!=max?dp[aim]:-1;     
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值