鸿 网 互 联 www.68idc.cn

当前位置 : 服务器租用 > 编程语言开发 > erlang > >

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

来源:互联网 作者:佚名 时间:2016-05-12 16:00
题目: 给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求组成aim的最少货币数。 举个例子 arr[5,2,3] ,aim=20 4张5元可以组成20,并且是最小的,所以返回4 arr[5,2,

题目:
给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求组成aim的最少货币数。
举个例子
arr[5,2,3] ,aim=20
4张5元可以组成20,并且是最小的,所以返回4
arr[5,2,3],aim=0。
不用任何货币就可以组成0元,这里返回0.
arr[5,2,3],ami=4
这里无法组成返回-1
思路:这里我们采用经典的动态规划的方法做,时间复杂度可以在O(n^2)。
经典动态规划的放法。如果arr的长度N,生成的行数为N,列数为aim+1的动态规划表dp。dp[i][j]的含义是,在可以任意使用arr[0..i]货币的情况下,
组成j所需的最小张数。根据这个定义,dp[i][j]有如下计算方法
1.dp[0..N-1][0]的值表示找的钱数为0的时候需要的最少张数,钱数为0时,完全不需要任何货币所以全部设置为0.
2.dp[0][0..aim]表示只能使用arr[0]货币的情况下找某个签署的最小张书。比如,arr[0]=2,那么能找开的钱数为2,4,6,8,…所以令dp[0][2]=1,dp[0][4]=2,…,
其他位置都是找不开的,其余一律设为32位整数最大值,我们把这个值记为max。
3.剩下的位置以此从左到右,再从上到下计算。
情况有如下:
1.不利用当前i的直接,即dp[i][j]=dp[i-1][j]的值
2.只使用1张当前货币arr[i]情况下最少的,即dp[i-1][j-arr[i]]+1
3.只使用2张当前货币arr[i]情况下最少的张书,即dp[i-1][j-2*arr[i]]+2
4.只使用n张当前货币arr[i]情况下最少的张数,即dp[i-1][j-n*arr[i]]+n
dp[i][j]=min{dp[i-1][j-k*arr[i]]+k}(k>=0)
这里我们把dp[i][j]提取出来
dp[i][j]=min{dp[i-1][j],dp[i-1][j-k*arr[i]]+k}(k>=1)
我们可以设k=x+1则有
dp[i][j]=min{dp[i-1][j],dp[i-1][j-arr[i]-x*arr[i]]+x+1}(x>=0)

dp[i-1][j-arr[i]-x*arr[i]]+x(x>=0)这个式子带入第一个可以得到dp[i][j-arr[i]]
所以dp[i][j]=min{dp[i-1][j],dp[i][j-arr[i]]+1},如果j小于arr[i].则发生越界,说明arr[i]太大,一张都会超过j,所以直接dp[i][j]=dp[i-1][j],代码如下:

 package 每日一题;

/**
 * 换钱的最少次数
 * 给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求组成aim的最少货币数。
 * 举个例子
 * arr[5,2,3] ,aim=20
 * 4张5元可以组成20,并且是最小的,所以返回4
 * arr[5,2,3],aim=0。
 * 不用任何货币就可以组成0元,这里返回0.
 * arr[5,2,3],ami=4
 * 这里无法组成返回-1
 *
 * Created by lz on 2016/5/10.
 */

public class mintimes {
    /**
     * 经典动态规划的放法。如果arr的长度N,生成的行数为N,列数为aim+1的动态规划表dp。dp[i][j]的含义是,在可以任意使用arr[0..i]货币的情况下,
     * 组成j所需的最小张数。根据这个定义,dp[i][j]有如下计算方法
     * 1.dp[0..N-1][0]的值表示找的钱数为0的时候需要的最少张数,钱数为0时,完全不需要任何货币所以全部设置为0.
     * 2.dp[0][0..aim]表示只能使用arr[0]货币的情况下找某个签署的最小张书。比如,arr[0]=2,那么能找开的钱数为2,4,6,8,...所以令dp[0][2]=1,dp[0][4]=2,...,
     * 其他位置都是找不开的,其余一律设为32位整数最大值,我们把这个值记为max。
     * 3.剩下的位置以此从左到右,再从上到下计算。
     * 情况有如下:
     * 1.不利用当前i的直接,即dp[i][j]=dp[i-1][j]的值
     * 2.只使用1张当前货币arr[i]情况下最少的,即dp[i-1][j-arr[i]]+1
     * 3.只使用2张当前货币arr[i]情况下最少的张书,即dp[i-1][j-2*arr[i]]+2
     * 4.只使用n张当前货币arr[i]情况下最少的张数,即dp[i-1][j-n*arr[i]]+n
     * dp[i][j]=min{dp[i-1][j-k*arr[i]]+k}(k>=0)
     * 这里我们把dp[i][j]提取出来
     * dp[i][j]=min{dp[i-1][j],dp[i-1][j-k*arr[i]]+k}(k>=1)
     * 我们可以设k=x+1则有
     * dp[i][j]=min{dp[i-1][j],dp[i-1][j-arr[i]-x*arr[i]]+x+1}(x>=0)
     *
     * dp[i-1][j-arr[i]-x*arr[i]]+x(x>=0)这个式子带入第一个可以得到dp[i][j-arr[i]]
     * 所以dp[i][j]=min{dp[i-1][j],dp[i][j-arr[i]]+1},如果j小于arr[i].则发生越界,说明arr[i]太大,一张都会超过j,所以直接dp[i][j]=dp[i-1][j]
     * @param arr
     * @param aim
     * @return
     */
    public int minCoins(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 (int j = 0; j <=aim; j++) {
            dp[0][j]=max;
            if (j-arr[0]>=0 && dp[0][j-arr[0]]!=max){
                dp[0][j]=dp[0][j-arr[0]]+1;
            }
        }
        int left = 0 ;
        for (int i = 0; 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(dp[i-1][j],left);
            }
        }
        return dp[n-1][aim] !=max ?dp[n-1][aim] : -1;
    }
}
网友评论
<