问题一
1.问题描述
假设有几种硬币,如1、3、5,并且数量无限。请找出能够组成某个数目的找零所使用最少的硬币数。
2.问题分析
动态规划的思想,用 dp[] 存放自底向上问题的解。dp[] 大小为 amount,dp[k] = min(dp[k – coin[i]) + 1
3.Java实现
public int coinChange(int[] coins, int amount) {
int max = amount + 1;
int[] dp = new int[amount + 1];
//先令 dp[] 初始化为 amount + 1
Arrays.fill(dp, max);
//当 amount = 0 时,解也是 0
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}
//如果 dp[amount] > amount,即还是初始化的值,说明无解
return dp[amount] > amount ? -1 : dp[amount];
}
问题二
1.问题描述
将上题中的求最小的硬币数,改成所有的方案。即,假设有几种硬币,并且数量无限。请找出能够组成某个数目的找零所有的方案数。
2.问题分析
用 dp[i, j] 表示:使用 第 1,2,…i 种面值的硬币时,需要找金额为 j 的钱,最多可采用多少种不同的方式。
i 表示可用的硬币种类数, j 表示 需要找回的零钱
有两种情况:对于某种面值的硬币,要么使用了它,dp[i, j – coins[i]]
要么不使用它,dp[i – 1, j]
所以,dp[i, j] = dp[i, j – coins[i]] + dp[i – 1, j]
3.Java实现
public int coinChangeWays(int[] coins, int n){
int m = coins.length;
int[][] dp = new int[m+1][n+1];
//第一列 dp[i][0] 表示用coins[i]组成面额为0的钱的方案,即不需要任何货币,初始化为1
for(int i = 0; i <= m; i++)
dp[i][0] = 1;
//第一行 dp[0][j] 没有货币,初始化为0
for(int i = 1; i <= n; i++)
dp[0][i] = 0;
for(int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++){
if (j - coins[i] >= 0)
dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]];
else
dp[i][j] = dp[i - 1][j];
}
}
return dp[m][n];
}
问题三
1.问题描述
将上题中硬币无限量使用,改为每个面值只有一个硬币。即,假设有几种硬币,每种硬币只有一个。请找出能够组成某个数目的找零所有的方案数。
题目还可以这样,同一个意思:
给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。
2.问题分析
也是两种方法:要么加这个数,dp[i – 1, j – weight[i]]。和上题不同的只是,加上这个数,i 也要减一。
要不不加这个数,dp[i – 1, j]
所以,dp[i, j] = dp[i-1, j] + dp[i-1, j-weight[i]]
3.Java实现
public int sunWays(int[] weight, int sum) {
int n = weight.length;
int[][] dp = new int[n+1][sum+1];
for (int i = 0 ; i < n ;i++) {
dp[i][0] = 1;
}
for (int j = 1 ; j < sum ;j++) {
dp[0][j] = 0;
}
for (int i = 1 ; i <= n ;i++) {
for (int j = 0 ; j <= sum ;j++) {
if(weight[i] <= j)
dp[i][j] = dp[i-1][j] + dp[i-1][j-weight[i]];
else
dp[i][j] = dp[i-1][j];
}
}
return dp[n][sum];
}
上面的方法,dp[][]用了二维数组, 也可以用一维数组来节省空间。
public int sumWays(int[] weight, int sum){
int dp[]=new int[sum+1];
dp[0]=1;
for(int i = 0; i < weight.length; i++){
for(int j = sum; j >= weight[i]; j--){
dp[j] = dp[j - weight[i]] + dp[j];
}
}
return dp[sum];
}