class Solution {
public int coinChange(int[] coins, int amount) {
/**
因为硬币是可以重复使用的,所以是一个完全背包问题,完全背包问题只需要将0-1背包的逆序遍历dp数组改成正序遍历即可
*/
if(amount ==0) return 0;
int[] dp = new int[amount+1];
for(int coin :coins){
// 将逆序遍历改成正序遍历
for(int j = coin;j<=amount;++j){
if(j == coin){
// 如果j恰好等于当前面值coin,那就当前j下要一个硬币coin
dp[j] = 1;
}else if(dp[j] ==0 && dp[j-coin] != 0){dp[j] = dp[j-coin]+1;}
else if(dp[j-coin] != 0){dp[j] = Math.min(dp[j],dp[j-coin]+1);}
}
}
// 如果amount金额下有满足条件的硬币个数,就返回。否则返回-1
return dp[amount] == 0 ? -1 :dp[amount];
}
}
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount+1];
dp[0] =1;
for(int coin:coins){
for(int j =coin;j<=amount;++j){
// 之前做完全背包问题是比较并只存储一个要么数量最大或最小的个数,这里是全部都要即累加
dp[j] = dp[j] +dp[j-coin] ;
}
}
return dp[amount];
}
}
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
/**
完全背包问题
*/
int n = s.length();
boolean[] dp = new boolean[n+1];
dp[0] =true;
// 这里的背包是s字符串,物品是数组中的每一个元素,向背包里面装物品
for(int i=1;i<=n;++i){ // 外层循环是背包
for(String word:wordDict){ // 内层循环是物品
int len = word.length();
if(len<=i && word.equals(s.substring(i-len,i))){ // 表示物品小于背包容量, 对应包的这一部分 i-len,i
// 要么装包要么不装
dp[i] = dp[i] || dp[i-len];
}
}
}
return dp[n];
}
}
class Solution {
public int combinationSum4(int[] nums, int target) {
/**
完全背包+组合总和
*/
int[] dp = new int[target+1];
dp[0] =1;
Arrays.sort(nums);
//外层是背包
for(int i =1;i<= target;++i){
// 内层是物品,遍历nums找到所有的可能不超过容量i的物品,累加所有能得到的组合
for(int j=0;j<nums.length && nums[j]<=i ;++j){ //nums[j]<i 当前的物品需要小于容量i,才不会爆仓
dp[i] = dp[i] + dp[i-nums[j]] ;//dp[i-nums[j]] 是前序的
}
}
return dp[target];
}
}