面试题,硬币--动态规划

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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/jump_into_zehe/article/details/106559932