LeetCode打卡——322.零钱兑换
题目描述:
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例:
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
思路:
涉及到选与不选的问题,很明显可以用动态规划来解,我们可以令result[i][j]来表示仅考虑前i中硬币,且剩余的总金额为j的情况的最优解,接下来寻找状态转移方程。当i=1时,即仅考虑第一个硬币的情况下,问题的解即取决于j是否能整除i。当i>1时,问题的解为不考虑第i中硬币的情况和考虑k个第i种硬币的情况比较后得到的最小值(1<=k<=maxCount,maxCount=j/coins[i]),因此可以得到如下状态转移方程:
r
e
s
u
l
t
[
i
]
[
j
]
=
{
m
i
n
{
r
e
s
u
l
t
[
i
−
1
]
[
j
]
,
r
e
s
u
l
t
[
i
−
1
]
[
j
−
k
∗
c
o
i
n
s
[
i
]
]
+
k
}
,
i>1,k从1取到maxCount
j
%
i
=
=
0
?
j
/
i
:
−
1
,
i=1
result[i][j]= \begin{cases} min\{result[i-1][j],result[i-1][j-k*coins[i]]+k\},& \text{i>1,k从1取到maxCount}\\ j\%i==0?j/i:-1,& \text{i=1} \end{cases}
result[i][j]={min{result[i−1][j],result[i−1][j−k∗coins[i]]+k},j%i==0?j/i:−1,i>1,k从1取到maxCounti=1
代码:
class Solution {
public int coinChange(int[] coins, int amount) {
int sumCount = coins.length;//硬币总数
int[][] result = new int[sumCount+1][amount+1];//问题的解
for (int i = 1;i <= sumCount;i++){//初始化j为0的情况下问题的解为0
result[i][0] = 0;
}
for (int j = 1;j <= amount;j++){//初始化i为1的情况
result[1][j] = j%coins[0] == 0 ? j/coins[0] : -1;
}
for (int i = 2;i <= sumCount;i++){
for (int j = 1;j <= amount;j++){
result[i][j] = result[i-1][j];//不考虑第i种硬币的情况
int countI = 1;
while (j-countI*coins[i-1] >= 0){//考虑第i中硬币的情况下
int temp = result[i-1][j-countI*coins[i-1]];
if (temp == -1){//判断当前countI的取值是否有解
countI++;
continue;
}
temp += countI;//若有解则加上硬币数
if (temp < result[i][j] || result[i][j] == -1){
//判断该种取法是否小于当前最优解
result[i][j] = temp;
}
countI++;
}
}
}
return result[sumCount][amount];
}
}
优化:
代码提交后通过了,但效率并不是很高,算法一共有三层循环,外两层循环分别取决于硬币数量和总金额,而第三层循环则取决于每枚硬币的面值,我们可以通过将问题result[i][j]转化为result[i][j-coins[i]]+1来删去该层循环,优化后的状态转移方程为:
r
e
s
u
l
t
[
i
]
[
j
]
=
{
m
i
n
{
r
e
s
u
l
t
[
i
−
1
]
[
j
]
,
r
e
s
u
l
t
[
i
]
[
j
−
c
o
i
n
s
[
i
]
]
+
1
}
,
i>1
j
%
i
=
=
0
?
j
/
i
:
−
1
,
i=1
result[i][j]= \begin{cases} min\{result[i-1][j],result[i][j-coins[i]]+1\},& \text{i>1}\\ j\%i==0?j/i:-1,& \text{i=1} \end{cases}
result[i][j]={min{result[i−1][j],result[i][j−coins[i]]+1},j%i==0?j/i:−1,i>1i=1
代码如下:
class Solution {
public int coinChange(int[] coins, int amount) {
int sumCount = coins.length;//硬币总数
int[][] result = new int[sumCount+1][amount+1];//问题的解
for (int i = 1;i <= sumCount;i++){//初始化j为0的情况下问题的解为0
result[i][0] = 0;
}
for (int j = 1;j <= amount;j++){//初始化i为1的情况
result[1][j] = j%coins[0] == 0 ? j/coins[0] : -1;
}
for (int i = 2;i <= sumCount;i++){
for (int j = 1;j <= amount;j++){
result[i][j] = result[i-1][j];//不考虑第i种硬币的情况
int countI = 1;
if (j - coins[i-1] < 0){
continue;
}
int temp = result[i][j-coins[i-1]];//考虑取一枚第i中硬币
if (temp == -1){//判断是否有解
continue;
}
temp++;
if (temp < result[i][j] || result[i][j] == -1){
result[i][j] = temp;//比较去最小值
}
}
}
return result[sumCount][amount];
}
}