提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
完全背包:
动态转移方程满足:dp[i][j]=max{dp[i-1][ j-k×v[i] ] + k×w[i]},其中 0 ≤ k×v[i] ≤ j
可以发现,当k只能取0、1时的特例就是简单的0-1背包问题。
可以发现,选0次,即不选是肯定存在的,dp[i][j]=dp[i-1][j];
然后选1次,那就不一定存在了,可能可以选,也可能不可以选,假设可以选,在选0次该物品求出dp[i][j]的基础上,确定是否选该物品:if (j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
选2次、选3次,依次类推,一直到选k次。
提示:以下是本篇文章正文内容,下面案例可供参考
一、 零钱兑换
public class Solution {
/**
* leetcode: 优化dp,用max而不是Integer.MAX_VALUE(因为int最大值+1 会溢出)
* f[i]表示凑成i需要的次数
*/
public int coinChange(int[] coins, int amount) {
int max = amount + 1;
int[] f = new int[amount + 1];
Arrays.sort(coins);
Arrays.fill(f, max);
f[0] = 0;
for (int i = 1; i <= amount; i++) {
//从后往前取,每次取到的都是最大的coin,次数最少
for (int j = 0; j < coins.length && coins[j] <= i; j++) {
f[i] = Math.min(f[i], f[i - coins[j]] + 1);
}
}
return f[amount] == max ? -1 : f[amount];
}
}
二、 零钱兑换||
同时硬币相当于我们的物品,每种硬币可以选择「无限次」,很自然的想到「完全背包」。
这时候可以将「完全背包」的状态定义搬过来进行“微调”:
定义 f[i][j] 为考虑前 i 件物品,凑成总和为 j 的方案数量。
为了方便初始化,我们一般让 f[0][x] 代表不考虑任何物品的情况。
因此我们有显而易见的初始化条件:f[0][0] = 1,其余 f[0][x] = 0。
代表当没有任何硬币的时候,存在凑成总和为 0 的方案数量为 1;凑成其他总和的方案不存在。
当「状态定义」与「基本初始化」有了之后,我们不失一般性的考虑 f[i][j]f[i][j] 该如何转移。
在二维解法的基础上,直接取消「物品维度」
确保「容量维度」的遍历顺序为「从小到大」(适用于「完全背包」)
将形如 f[i - 1][j - k * val]
的式子更替为 f[j - val]
,同时解决「数组越界」问题
class Solution {
public int change(int cnt, int[] cs) {
int n = cs.length;
int[] f = new int[cnt + 1];
f[0] = 1;
for (int i = 1; i <= n; i++) {
int val = cs[i - 1];
for (int j = val; j <= cnt; j++) {
f[j] += f[j - val];
}
}
return f[cnt];
}
}
三、数位成本和为目标值的最大数字
状态表示 :f[i][j] :前i项中任意选,且总代价恰好是j的最大数字为 f
状态计算 :f[i][j] = max_s(f[i - 1][j] , to_string(i) + f[i - 1][j - cost[i - 1]] );
上面和一般的完全背包没有太大的区别,主要是i为什么要放在前面?
答:是因为我们是从小到大枚举下标的,那么越往后下标越大,越大的数,位数越前,那整体肯定就越大 。
class Solution {
public:
string f[5005] ;
string largestNumber(vector<int>& cost, int t) {
fill_n(f,t + 1,"0"); // 0表示不合法
f[0] = "1"; // 只有从前0个中选0个合法 为 1
for(int i = 1;i <= 9 ; ++ i) // 完全背包模型
for(int j = cost[i - 1];j <= t; ++ j)
f[j] = max_s(f[j] , to_string(i) + f[j - cost[i - 1]] );
if(f[t].back() == '0') return "0"; // 从0过来的 都不合法
return f[t].substr(0,f[t].size() - 1); // 去掉1
}
string max_s (const string & a,const string & b){
if(a.back() == '0') return b; // 不合法的都是最小的
if(b.back() == '0') return a;
if(a.size() != b.size()) return a.size() > b.size() ? a:b;
return max(a,b);
}
};
总结
南无阿你拖佛!!!