#完全背包问题
一个背包总容量为V,现在有N种物品,第i种物品体积为w[i],价值为v[i],每个物品都有无限多件,现在往背包里面装东西,怎么装能使背包的内物品价值最大?
【分析】
完全背包问题和之前的硬币问题如出一辙,都是DAG上的动态规划问题,只需要对硬币问题的代码稍微修改,即可解决完全背包问题。
值得注意的是d的初始化是0,而不是-INF,如果是-INF,这个是关键,因为d(i)表示的是从i状态到小于等于i的所有状态的最长路,相当于从所有小于i的状态进行递推。
#include
using namespace std;
const int N=5;//物品数目
const int V=10;//背包容量
int main()
{
int w[10]={-1,3,3,3,3,3};
int v[10]={-1,1,1,1,1,1};
int dp[15]={0};
for(int i=1;i<=N;i++)
for(int j=1;j<=V;j++)
if(w[i]<=j) dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
cout<
【例题 Piggy-Bank HDU - 1114】
裸露的完全背包问题,和上面说的不同的是,这个也是必须从状态V到状态0。
#include
using namespace std;
const int maxn = 600;
const int INF = (1<<30);
int T;
int E,F,S;
int n;
int v[maxn],w[maxn];
int dp[10005];
int main()
{
cin>>T;
while(T--){
cin>>E>>F;
S=F-E;
cin>>n;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];
}
for(int i=1;i<=S;i++){
dp[i]=INF;
}
dp[0]=0;
for(int i=1;i<=S;i++){
for(int j=1;j<=n;j++){
if(i>=w[j]){
dp[i]=min(dp[i],dp[i-w[j]]+v[j]);
}
}
}
if(dp[S]==INF){
cout<<"This is impossible."<
#0-1背包问题
有编号分别为a,b,c的五件物品,它们的重量分别是1,2,3它们的价值分别是11,10,12。现在给你个承重为5的背包,如何让背包里装入的物品具有最大的价值总和?
【问题分析与解决方案】
1)与完全背包对比。0-1背包问题中,(与完全背包相比)每种物品只有一个,无法采用DAG动态规划。
2)与贪心法对比。初看0-1背包问题,笔者首先想到的是贪心策略,但是细细分析一下,方知贪心策略不能解决0-1背包问题。现在就根据原题目说明,如果根据贪心法来做,每次应该选择(价值/重量)最大的物品,那么将选择重量为1,2的物品,得到的价值之和是21。而正确的解是选择重量为1,3的物品,得到价值之和是23。
为什么不能用贪心呢?
3)0-1背包动态规划方案。
<
w[i],即第i个物品的重量比重量为j的背包承重量大,第i个物品不能放,那么显然有d(i,j)=d(i-1,j)#include
using namespace std;
const int N=3;//物品数目
const int V=5;//背包承重量
int main()
{
int w[4]={-1,1,2,3};
int v[4]={-1,11,10,12};
int dp[10][10]={0};
for(int i=1;i<=N;i++){
for(int j=1;j<=V;j++){
if(j
可以打印输出,看每次内层for循环结束后对应的第i行的更新结果,得到如下表:
下面笔者把if-else语句合并,可以得到以下代码:
#include
using namespace std;
const int N=3;//物品数目
const int V=5;//背包承重量
int main()
{
int w[4]={-1,1,2,3};
int v[4]={-1,11,10,12};
int dp[10][10]={0};
for(int i=1;i<=N;i++){
for(int j=1;j<=V;j++){
dp[i][j]=dp[i-1][j];
if(j>w[i]){
dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+v[i]);
}
}
}
cout<
空间复杂度优化(付出的代价是一次次覆盖掉上一行的结果,中间过程无法保留):
分析上面代码及运行结果表,我们不难发现,结果表是一行一行的进行更新的,本行每个元素的更新仅仅受上一行且小于本元素所在列的元素和本行对应的物品的影响,考虑到这一点,我们能不能仅仅用一行来代替所有行呢?答案是可以的。我们完全可以用一个一维数组来代替二维数组,每更新一行,不再多用存储空间,而是覆盖掉原来的值。注意j要从V到1枚举,因为这样对于每一个i来说,利用i-1行的数据来更新当前的dp[j],再更新比j小的对应的dp,由于dp[j]不受比j小的对应的dp的影响,这样可以保证每次元素的更新得到的都是当前状态下的最优解。如下代码:
#include
#include
using namespace std;
const int N=3;//物品数目
const int V=5;//背包承重量
int dp[V+10];
int main()
{
int w[4]={-1,1,2,3};
int v[4]={-1,11,10,12};
memset(dp,0,sizeof(dp));
for(int i=1;i<=N;i++){
for(int j=V;j>=1;j--){
if(j>=w[i]){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
}
cout<
4)改动题目为恰好装满背包(达到背包的最大承重量)
#include
using namespace std;
const int INF=(1<<30);
const int N=3;//物品数目
const int V=5;//背包承重量
int dp[10][10];
int main()
{
int w[4]={-1,1,2,3};
int v[4]={-1,11,10,12};
for(int i=0;i<=N;i++) dp[i][0]=0;
for(int j=1;j<=V;j++) dp[0][j]=-INF;
for(int i=1;i<=N;i++){
for(int j=1;j<=V;j++){
if(j
打印下动态规划中的那张表看看如下:
#多重背包
一个背包,承重量为V。现有n种物品,第i种物品有num[i]个,第i种物品的价值是v[i],重量是w[i],问这个背包能容纳的最大价值是多少?
【分析】
研究过0-1背包后,我们会发现解决多重背包问题的一个明显的思路,就是把多重背包里面的某一种物品的num[i]个物品看成是i种物品,这样问题就是0-1背包问题了,自然顺利解决。直接上代码如下:
#include
#include
using namespace std;
const int N=3;
const int V=8;
int main()
{
int w[N+100]={-1,1,2,2};
int v[N+100]={-1,6,10,20};
int num[N+100]={-1,10,5,2};
int dp[N+100][V+10];
int n=N;//转换成0-1背包后,n将是物品种类数,也是物品个数
for(int i=1;i<=N;i++){
int cnt=num[i];
while(cnt>=2){
n++;
w[n]=w[i];
v[n]=v[i];
cnt--;
}
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
for(int j=1;j<=V;j++){
if(j