标签:
题目:
给定数组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;
}
}
标签:
原文地址:http://blog.csdn.net/li563868273/article/details/51364877