Leetcode 面试题
硬币
题目描述:给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
示例1:
输入: n = 5
输出:2
解释: 有两种方式可以凑成总金额:
5=5
5=1+1+1+1+1
示例2:
输入: n = 10
输出:4
解释: 有四种方式可以凑成总金额:
10=10
10=5+5
10=5+1+1+1+1+1
10=1+1+1+1+1+1+1+1+1+1
解法:动态规划
思路:看到这道题我们的第一个想法就是将各个面额可组成n
的情况进行相加,但是要如何实现这个转移方程呢?我们先看一个例子n=10
当n=10时,有四种组合,我们看一下这四种组合
1个10
2个5
5个1,一个5
10个1
我们从观察一下,发现使用较大面额的硬币是在递减的,一开始是使用一个面额为10的,后来就是使用两个面额为5,再使用一个面额为5,再下去就是全部都用面额为1的
我们写一个简单的状态转移方程:
F(10) = F(5,10)+F(5,0)
F(5,10) = F(1,10)+F(1,5)+F(1,0)
发现没有,F(5,10)–>F(5,0)就是一个有没有使用10面额的差别,如果使用了10面额,那么我们需要用5合层的就是F(5,10-10)
从上面我们可以得到完整的状态转移方程,用i表示第几种面额的硬币,v表示n,c表示第i种硬币的面额:
F(i,v) = F(i-1,v)+F(i-1,v-c)+F(i-1,v-2c)+...+F(i-1,v-kc)
v-kv>=0
上面的方程还能再简化为:
F(i,v) = F(i-1,v)+F(i,v-c)
因为我们需要用到的只是i-1即类似这样的数据F(i,v) = F(i-1,v)+F(i-1,v-c)+F(i-1,v-2c)+...+F(i-1,v-kc)
,所以我们用一个一维数组f来保存这些数据
for(i=coin; i<=n; i++)
f[i]=(f[i]+f[i-coin])%mod;
有没有觉得f[i]=(f[i]+f[i-coin])
这段代码很熟悉,其实这段代码就对应了前面的状态转移方程F(i,v) = F(i-1,v)+F(i,v-c)
,只是因为f(i)保存的其实是f[i-1]的值,这一步就是将其更新
完整代码:
int waysToChange(int n){
int f[n+1];
int i;
for(i=0; i<=n; i++)
f[i]=0;
f[0]=1;
int mod = 1000000007;
int coins[4]={25,10,5,1};
for(int c=0; c<4; c++)
{
int coin=coins[c];
for(i=coin; i<=n; i++)
f[i]=(f[i]+f[i-coin])%mod;
}
return f[n];
}
心得体会
感觉动态规划还是很难啊,这道题想了挺久的,就觉得跟前面的题一样,很难想出一个很正确的转移方程,这道题之前也做过类似的,背包问题,动态转移方程是一样的,打算写完这篇博客去看一下 《背包九讲》,希望能够体会一个动态规划的精髓
题目以及解题思路来自
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/coin-lcci/solution/ying-bi-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。