leecode更新
This commit is contained in:
parent
a768168a5b
commit
c732207ff8
|
|
@ -0,0 +1,152 @@
|
|||
package com.markilue.leecode.dynamic;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @BelongsProject: Leecode
|
||||
* @BelongsPackage: com.markilue.leecode.dynamic
|
||||
* @Author: markilue
|
||||
* @CreateTime: 2022-12-05 10:50
|
||||
* @Description: TODO 力扣518题 零钱兑换II:
|
||||
* 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
|
||||
* 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。
|
||||
* 假设每一种面额的硬币有无限个。
|
||||
* 题目数据保证结果符合 32 位带符号整数。
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class T12_Change {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
int amount = 5;
|
||||
int[] coins = {1, 2, 5};
|
||||
System.out.println(change2(amount, coins));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test1() {
|
||||
int amount = 3;
|
||||
int[] coins = {2};
|
||||
System.out.println(change1(amount, coins));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2() {
|
||||
int amount = 10;
|
||||
int[] coins = {10};
|
||||
System.out.println(change1(amount, coins));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自己思路:由于硬币可以无限使用,因此这题是完全背包问题 ->但这题求的是方法数,因此是一个完全背包的组合问题
|
||||
* TODO 动规五部曲:
|
||||
* (1) dp数组的定义: dp[i][j] 表示使用coins[0-i]的数背包容量为j时有dp[i][j]种方法可以组成
|
||||
* (2) dp数组的状态方程: dp[i][j]可以表示如果不用i硬币有几种方法组成+用i硬币有几种方法组成
|
||||
* dp[i][j]=dp[i-1][j]+dp[i-1][j-coins[i]]+dp[i-1][j-2*coins[i]]+...+
|
||||
* (3) dp数组的初始化:dp[i][0]=1;dp[0][i==coins[0]*k]=1
|
||||
* (4) dp数组的遍历顺序:外层硬币内层背包,从前往后
|
||||
* (5) 举例推导dp: 当test时 dp:
|
||||
* [0 1 2 3 4 5]
|
||||
* i=0: 1 1 1 1 1 1
|
||||
* i=1: 1 1 2 2 3 3
|
||||
* i=2: 1 1 2 2 3 4
|
||||
* 速度击败6.58%,内存击败5% 31ms
|
||||
*
|
||||
* @param amount
|
||||
* @param coins
|
||||
* @return
|
||||
*/
|
||||
public int change(int amount, int[] coins) {
|
||||
|
||||
int[][] dp = new int[coins.length][amount + 1];
|
||||
|
||||
//初始化
|
||||
for (int i = 0; i < coins.length; i++) {
|
||||
dp[i][0] = 1;
|
||||
}
|
||||
for (int i = 1; i < amount + 1; i++) {
|
||||
dp[0][i] = i % coins[0] == 0 ? dp[0][i] + 1 : 0;
|
||||
}
|
||||
|
||||
//动规遍历
|
||||
for (int i = 1; i < dp.length; i++) {
|
||||
for (int j = 1; j < amount + 1; j++) {
|
||||
if (j < coins[i]) dp[i][j] = dp[i - 1][j];
|
||||
else {
|
||||
int k = 0; //表示用几次coins[i]
|
||||
while (j - coins[i] * k >= 0) {
|
||||
dp[i][j] += dp[i - 1][j - coins[i] * k];
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return dp[coins.length - 1][amount];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自己思路::尝试一维dp数组(可以减少一层while循环),由于硬币可以无限使用,因此这题是完全背包问题 ->但这题求的是方法数,因此是一个完全背包的组合问题
|
||||
* TODO 动规五部曲:
|
||||
* (1) dp数组的定义: dp[j] 表示使用coins[0-i]的数背包容量为j时有dp[j]种方法可以组成
|
||||
* (2) dp数组的状态方程: dp[j]可以表示如果不用i硬币有几种方法组成+用i硬币有几种方法组成
|
||||
* dp[j]+=dp[j-coins[i]]
|
||||
* (3) dp数组的初始化:dp[0]=1;dp[i==coins[0]*k]=1
|
||||
* (4) dp数组的遍历顺序:外层硬币内层背包,从前往后
|
||||
* (5) 举例推导dp:当test时 dp:
|
||||
* [0 1 2 3 4 5]
|
||||
* i=0: 1 1 1 1 1 1
|
||||
* i=1: 1 1 2 2 3 3
|
||||
* i=2: 1 1 2 2 3 4
|
||||
* 速度击败99.93%,内存击败34.58% 2ms
|
||||
*
|
||||
* @param amount
|
||||
* @param coins
|
||||
* @return
|
||||
*/
|
||||
public int change1(int amount, int[] coins) {
|
||||
|
||||
int[] dp = new int[amount + 1];
|
||||
//初始化
|
||||
dp[0]=1;//初始化dp数组,表示金额为0时只有一种情况,也就是什么都不装
|
||||
//动规遍历
|
||||
for (int i = 0; i < coins.length; i++) {
|
||||
for (int j = coins[i]; j < amount + 1; j++) {
|
||||
dp[j]+=dp[j-coins[i]]; //少用coins[i]的硬币的方法之和
|
||||
}
|
||||
}
|
||||
return dp[amount];
|
||||
}
|
||||
|
||||
/**
|
||||
* 这种把背包容量放在外面的情况就有可能出现重复的情况:因为背包容量的每一个值,都是经过所有coins[i] 的计算,包含了{1, 5} 和 {5, 1}两种情况。
|
||||
* TODO 这种是求的排列数而不是组合数
|
||||
* 打印其dp数组可以发现,对于test来说,其dp数组为:1 1 2 3 5 9
|
||||
*
|
||||
* @param amount
|
||||
* @param coins
|
||||
* @return
|
||||
*/
|
||||
public int change2(int amount, int[] coins) {
|
||||
|
||||
int[] dp = new int[amount + 1];
|
||||
//初始化
|
||||
dp[0]=1;//初始化dp数组,表示金额为0时只有一种情况,也就是什么都不装
|
||||
//动规遍历
|
||||
for (int j = 0; j <= amount; j++) { // 遍历背包容量
|
||||
for (int i = 0; i < coins.length; i++) { // 遍历物品
|
||||
if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];
|
||||
//TODO 为什么这里会出现{1, 5} 和 {5, 1}两种情况?
|
||||
// 核心在于coins在里面这层循环,而dp[j] += dp[j - coins[i]];又用到了之前的dp[j],那么以前的dp[j]可是计算过coins【1和5】的情况,这里再conin[5和1]的情况就会重复
|
||||
// 而在外面这层循环就不会出现这种情况,因为coins是按顺序遍历的,这里的dp就只能考虑coin[1]在考虑coin[5]
|
||||
}
|
||||
}
|
||||
return dp[amount];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue