leetcode 面试题 08.11. 硬币(完全背包求方案数以及转移方程通俗推导)

硬币。给定数量不限的硬币,币值为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

原题戳我

这个就算是一个比较裸的完全背包求方案数的问题,和完全背包基本类似,唯一区别就是dp[i][j]代表用前i个物品正好组成j的方案数。

value[i]代表第i个物品的价值,很容易想到对于每个dp[i][j],我们可以去枚举使用多少个i物品,假设使用了k个i物品,那么dp[i][j]就加上了dp[i-1][j-k*value[i]],这个还是很好理解的。

但是这样的问题在于速度太慢,有很多的重复计算,举个例子,也是通俗的讲一下完全背包转移方程的推导过程。

假设要计算dp[5][10],,当前value[5] = 3,我们可以选择放1个,2个或者3个五号物品(0个五号物品先不考虑因为0个物品并没有什么重复计算),我们现在选择放2个五号物品,那么dp[5][10]应该加上dp[4][4]的方案数,这个应该是很显然的。

那么我们思考一下计算dp[5][7]的时候,我们也肯定是考虑过放1个五号物品,那么dp[5][7]肯定也加过dp[4][4]的方案数,这就是问题的关键,我们没必要在回到dp[4][4]了,因为dp[5][7]中已经包含的dp[4][4]!

所以对于dp[i][j],我们不去考虑要放多少个i号物品,而是换个角度思考问题,只要是用前i个物品凑成了j-value[i]的价值,那么我再放一个i号物品就凑成j的价值,所以dp[i][j]要加上dp[i][j-value[i]],转移方程就是这样得出来的。

class Solution {
    public int waysToChange(int n) {
        int mod = 1000000007;
        int rule[] = {1,5,10,25};
        int dp[][] = new int [4][n+1];
        for(int i=0;i<4;i++)
        {
            dp[i][0] = 1;
            for(int j=1;j<=n;j++)
            {
                if(i>0)
                    dp[i][j] = dp[i-1][j];
                if(j>=rule[i])
                    dp[i][j] = (dp[i][j]+dp[i][j-rule[i]])%mod;
            }
        }
        return dp[3][n];
    }
}

你可能感兴趣的:(南大java复试,线性dp,思维)