一个具体的找零钱问题:
参考:程序员面试再也不怕动态规划了,看动画,学DP,找零钱 (LeetCode 322)
硬币面值:1,2,5,7,10
找零金额:14
- step1:定义长度为15的dp数组
所有元素初始化为-1, d p [ 0 ] = 0 dp[0] = 0 dp[0]=0
- step2:遍历coins列表找出小于金额的硬币面值
j
j
j,接着再使用
d
p
[
i
−
c
o
i
n
s
[
j
]
]
dp[i-coins[j]]
dp[i−coins[j]]来找出
i
−
c
o
i
n
s
[
j
]
i-coins[j]
i−coins[j]的最优解;最终金额
i
i
i的最优解为
1
+
d
p
[
i
−
c
o
i
n
s
[
j
]
]
1+dp[i-coins[j]]
1+dp[i−coins[j]]。
d p [ 1 ] = d p [ 0 ] + 1 = 1 dp[1]=dp[0]+1=1 dp[1]=dp[0]+1=1
d p [ 2 ] = d p [ 0 ] + 2 dp[2]=dp[0]+2 dp[2]=dp[0]+2、 d p [ 2 ] = d p [ 1 ] + 1 dp[2]=dp[1]+1 dp[2]=dp[1]+1,而 d p [ 0 ] < d p [ 1 ] dp[0]<dp[1] dp[0]<dp[1],故 d p [ 2 ] = d p [ 0 ] + 2 = 1 dp[2]=dp[0]+2=1 dp[2]=dp[0]+2=1
d p [ 3 ] = 1 + d p [ 2 ] dp[3]=1+dp[2] dp[3]=1+dp[2]、 d p [ 3 ] = 2 + d p [ 1 ] dp[3]=2+dp[1] dp[3]=2+dp[1],而 d p [ 1 ] = d p [ 2 ] = 1 dp[1]=dp[2]=1 dp[1]=dp[2]=1,故 d p [ 3 ] = 1 + 1 = 2 dp[3]=1+1=2 dp[3]=1+1=2
d p [ 4 ] = 1 + d p [ 3 ] dp[4]=1+dp[3] dp[4]=1+dp[3]、 d p [ 4 ] = 2 + d p [ 2 ] dp[4]=2+dp[2] dp[4]=2+dp[2],而 d p [ 2 ] < d p [ 3 ] dp[2]<dp[3] dp[2]<dp[3],故 d p [ 4 ] = 2 dp[4]=2 dp[4]=2
d p [ 5 ] = d p [ 0 ] + 1 = 1 dp[5]=dp[0]+1=1 dp[5]=dp[0]+1=1
d p [ 6 ] = 1 + d p [ 5 ] dp[6]=1+dp[5] dp[6]=1+dp[5]、 d p [ 6 ] = 2 + d p [ 4 ] dp[6]=2+dp[4] dp[6]=2+dp[4]、 d p [ 6 ] = 5 + d p [ 1 ] dp[6]=5+dp[1] dp[6]=5+dp[1]
同理,计算出剩下所有金额的最优解:
C++版
using namespace std;
class Solution{
public:
int coinChange(vector<int>&coins,int amount){
vector<int>dp(amount+1,-1);
dp[0]=0;
for(int i=1;i<=amount;i++){
for(int j=0;j<coins.size();j++){
if(coins[j]<=i&&dp[i-coins[j]!=-1){
//如果当前金额还未计算或者dp[i]比正在计算的解大,则更新最优解dp[i]
if(dp[i]==-1||dp[i]>dp[i-coins[j]]+1)
dp[i]=dp[i-coins[j]]+1;
}
}
}
return dp[amount] //返回金额amount的最优解
}
}
python版
采用动态规划编写一个找零钱函数dpMakeChange,接收硬币面值列表coinValueList和找零金额change两个参数,返回change对应的最少硬币数的找零详情字典(包含所用的每种硬币面值和个数)。
def dpMakeChange(coinValueList, change):
minCoins = [0] * (change + 1)#初始化minCoins列表为0
coinsUsed = {}#保存的是找零钱change最后一枚找的币值
for cents in range(change + 1):
coinCount = cents#初始化硬币数量为金额cents
newCoin = 1
#遍历硬币列表coinValueList,遍历小于金额cents的硬币面值
for j in [c for c in coinValueList if c <= cents]:
#如果当前正在求的最优解小于当前金额的coinCount,则更新coinCount值
if minCoins[cents - j] + 1 < coinCount:
coinCount = minCoins[cents - j] + 1
newCoin = j
minCoins[cents] = coinCount#把最优的coinCount值放入minCoins表中
coinsUsed[cents] = newCoin
changeDetails = {}#保存找零金额change对应最少硬币数的找零详情字典
while change > 0:
thisCoin = coinsUsed[change]
if thisCoin in changeDetails:
changeDetails[thisCoin] += 1
else:
changeDetails[thisCoin] = 1
change -= thisCoin
return changeDetails