题目一
给定数组arr,数组中有N个元素,其中所有的之都为整数且不重复.每个只代表一种面值的货币,每种面值的货币可以使用任意张,在给定一个整数aim代表要找的钱数,求组成aim的最少货币数.
解法
依然是用二维数组分析的动态规划问题,数组dp长为N行aim-1列.其中dp[i][j]代表用arr[0...i]中出现的面值构成钱数为j所需要的钱的最小张数.那么就有
dp[i][j] = min{dp[i-1][j], dp[i][j - arr[i]] + 1}
整个过程中时间复杂度和空间复杂度分别为O(N*aim)
代码
int minCoins1(int arr[], int length, int aim) {
if (length == 0 || aim == 0)
return 0;
if (aim < 0)
return -1;
int maxInt = INT_MAX;
int dp[length][aim + 1];
for (int j = 1; j < aim + 1; j ++) { //初始化第一行
if (j - arr[0] >=0 && j % arr[0] == 0)
dp[0][j] = j / arr[0];
else
dp[0][j] = maxInt;
}
//从左到右,从上倒下计算dp矩阵
for (int i = 1; i < length; i ++) {
for (int j = 1; j <= aim; j ++) {
if (j - arr[i] >= 0 && dp[i][j - arr[i]] != maxInt)
dp[i][j] = min(dp[i - 1][j], dp[i][j - arr[i]] + 1);
else
dp[i][j] = dp[i - 1][j];
}
}
// for (int i = 0; i < length; i ++) {
// for (int j = 0; j < aim + 1; j ++)
// cout<<dp[i][j]<<" ";
// cout<<endl;
// }
return dp[length - 1][aim] != maxInt ? dp[length - 1][aim]:-1;
}
输入
arr={5,2,3} 20
arr={3,5} -1
输出
4
-1
题目二
给定数组arr,arr中所有的值都为整数.每个值仅代表一张钱的面值,在给定一个整数aim代表要找的钱数, 求组成aim的最少货币数.
解法
同上面一样,我们需要的dp矩阵为N行aim+1列.dp[i][j]代表用arr[0...i]中的钱组成钱数为j所用的最小张数张数.和题目一不同的是根据题意,这里是不允许重复使用面值的.所以dp矩阵初始化第一行是不同的.但是他们有相同的转移方程:
dp[i][j] = min{dp[i-1][j], dp[i][j - arr[i]] + 1}
复杂度也和题目一相似
代码
int minCoins2(int arr[], int length, int aim) {
if (aim < 0)
return -1;
if (length == 0 || aim == 0)
return 0;
int maxInt = INT_MAX;
int dp[length][aim + 1];
for (int i = 0; i < aim + 1; i ++) { //初始化第一行
if (i == arr[0])
dp[0][i] = 1;
else
dp[0][i] = maxInt;
}
for (int i = 1; i < length; i++) {
for (int j = 1; j <= aim; j++) {
if (j - arr[i] >= 0 && dp[i - 1][j - arr[i]] != maxInt)
dp[i][j] = min(dp[i - 1][j - arr[i]] + 1, dp[i - 1][j]);
else
dp[i][j] = dp[i - 1][j];
}
}
return dp[length - 1][aim] != maxInt ? dp[length - 1][aim]:-1;
}
输入
arr={5,2,3}, aim = 20
arr = {5,2,5,3},aim = 15
输出
-1
4
拓展
上面两题都是用的经典动态规划的方式,使用的二维数组.一个常用的优化方式是使用长度为N的一维数组滚动求取,节省了空间开销