leetcode面试8.10 硬币
题目
给定数量不限的硬币,币值为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
思路
先来一个二维数组
1 2 3 4 5 ....n
1 1 1 1 1 1 ....1
5 1 1 1 1 2 ....
10 1 1 1 1 2 ....
25 1 1 1 1 2 ....
先有个印象哈,虽然这个很简单,左边是硬币,上边是要求给定的n
1>思路一:二维数组实现dp
dp[][]=new dp[5][n+1];coins={1,5,10,25}
这里二维数组i的位置是5而不是4,是因为下面i的循环要从1开始,如果从0开始就会越界,想不通就可以看下面的代码
则有一些位置是可以初始化的,比如第一列将其初始化为0
随后开始正常的二层循环
这个循环一定是硬币这边在外层,这样代表的是现在能用的硬币是哪个(背包问题的解法就是此刻能往里面放的是哪个东西,就是这里能用的硬币是哪个),你如果放在里面就乱了
里面的状态方程应当是
如果当前的数值小于当前的coins[i-1],则输入的是上面那一个dp[i-1][j]
否则dp[i][j]=dp[i-1][j]+dp[i][j-coins[i-1]]这里并没有用max什么的,这里是求次数,自然是用+的
从这里我们就考虑到了一个问题,假如是n=5,现在也正好用到了硬币5
那么上面的dp[i][j]=dp[i-1][j]+dp[i][j-coins[i]]就变成了dp[1][5]=dp[1][5]+dp[5][0],后面这个变成了0
显然是不太合适的
因为这个的含义应该是刚好使用了一个硬币5
所以就发现了在这动态规划中0位置的元素不能让他们躺尸了,其现在代表的含义是恰好可以用一张的情况,所以将其全部变成1
for(int i=0;i<coins.length;i++) dp[i][0]=1;
int[][] dp=new int[5][n+1];
int[] coins={1,5,10,25};
for (int i = 0; i <5 ; i++) {
dp[i][0]=1;
}
for (int i = 1; i <5 ; i++) {
for (int j = 1; j <=n ; j++) {
if (j<coins[i-1]){//注意这里是i-1,因为i是从1开始的
dp[i][j]=dp[i-1][j];
}else {
dp[i][j]=(dp[i-1][j]+dp[i][j-coins[i-1]])%1000000007;
}
}
}
return dp[4][n];
2> 思路2:采用一维数组实现dp
虽是一维数组本质也是dp的思想
和上面个dp[i][0]=1的情况一样,这里的dp[0]=1,也是用来表示用这一个硬币的情况下
dp[]=new dp[n+1];
coins={1,5,10,25};
这里的方程依然是coins的循环在外面,n在里面
dp[i]=dp[i]+dp[i-coins[j]]
int[] dp = new int[n + 1];
dp[0] = 1;
int[] coins = {1, 5, 10, 25};
for (int coin : coins) {
for (int i = 1; i <=n; i++) {
if (i < coin) {
dp[i] = dp[i];//这里可不能写break,那样的话就是在coin=x的时候,一开始这个循环就结束了,不会进行后面的操作了
//所以就可以理解,这里直接将i从coin开始,就不用有这个问题了,因为比coin小的都是上一个coin的,不变,变化是从等于coin的这一个开始,也就是说i=coin的话,这个if语句删了就可以
} else {
dp[i] = (dp[i] + dp[i - coin]) % 1000000007;
}
}