本次要讲的是完全背包问题的详解:他和01背包有很多相同之处;
可以参考我之前的01背包,然后就能更好的理解这个了。
首先介绍问题,现有物品a,b,c三样,价格分别为{1,4,3};质量分别为{1,3,2},现在求一个背包容量为n的背包怎么装物品的价值为最大;商品数量为无限,01背包问题和完全背包问题的差别就在于01只能放1次,完全背包可以放多次。
那么我们似乎就可以得到他的算法
int dp[背包容量+1];//背包最大价值
int v[],w[];//物品质量、物品价值
for(int i=0;i<物品种类;i++)
{
for(int j=v[i];k<=背包总容量;j++)
{
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
其中状态转移方程为f[m]=max(f[m],f[m-v [ i ] ]+w[i] ),看着好像有点难以理解,不妨我们来证明一下为什么会得到这个;
首先说一下为什么式从小到大递增,如果从大到小,你是无法把所有的情况都考虑进去的,你当前所获取k*v[i]的在你之前的子问题里没有解。然而从小到大枚举,会逐层递增上去,可以遍历所有的情况。
然后我们证明一下这个状态转移方程,为什么正确,假设当某时刻的 j 里,必然包含着k个v[i],
f[ j ]= f[ j - kv[i] ]+kw[i]
那么他必然是由
f[j-(k-1)*v[i]]+(k-1)*w[i]
转移过来的,
然后依次转移他们的值其实来自于
f[j-v[i]]+w[i]
那么f[j-v[i]]+w[i]必然可以由于i值的变化递推到f[ j - k* v[i] ]+k*w[i];
动态规划思路就是由子问题传递,然后通过上层的子问题所得的最优解一步一步推到整个父问题的最优解。
下面贴上我java的代码
public static void main(String[] args) {
int[] v={1,3,3};//价格
int[] w={1,4,2};//重量
int n=3;//背包容量
int[] bag=new int[n+1];
for(int i=0;i<v.length;i++)
{
for(int j=w[i];j<=n;j++)
{
bag[j]=Math.max(bag[j],bag[j-w[i]]+v[i]);
}
}
System.out.println(bag[n]);
}
介绍完了问题我们不妨做个简单的问题练练手,来自leetcode的动态规划算法题
给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
就不做详细分析了典型的动态规划完全背包类问题;
int query(int n)
{
int[] coins={1,5,10,25};
int[] dp=new int[n+1];
dp[0]=1;
for(int coin:coins){
for(int i=coin;i<=n;i++)
{
dp[i]=(dp[i]+dp[i-coin])%1000000007;
}
}
return dp[n];
}