https://www.cnblogs.com/Mychael/p/8282894.html
01背包
有一个体积为V的背包,有n个物品,每个物品都有体积vi和价值wi,在背包体积范围内,求能桌下的最大价值。
这个问题中每个物品只能用一次。
设dp[i][j]表示用前i个物品装体积为j的背包。
那么第i个物品要么装要么不装:
1、如果不装,第i个物品和没有一样,dp[i][j]=dp[i-1][j]
2、如果装,等于dp[i-1[j]中刨除第i个物体体积此时的最大价值 加上 第i个物体的价值。dp[i][j]=dp[i-1][j-v[i]]+w[i];
我们只需取这两种方案的最大值,就算是解决了。dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
时间复杂度是O(V*M),空间也是O(V*M)
其实空间可以优化
注意观察,可以发现每个状态dp[i][j]之和dp[i-1][]有关,什么i-2,i-3都没用了
我们试着开一个一维的数组dp[M];
一维难道不会覆盖掉原来的值么?
会的,但我们优先覆盖不用的值。怎么说呢?就是我们每次只会用比j小的地方的值,比j大的地方是不用到的,所以我们只要j从后往前枚举,就不会冲突了。
dp[j]=max(dp[j],dp[j-v[i]]+w[i]) [j=N->v[i]]
具体来看看代码:
for(int i=1;i<=N;i++)
for(int j=V;j>=v[i];j--)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
自写代码 二维记忆数组
#include <iostream>
#include<cstdio>
#define MAX_N 10001
#define MAX_M 1001
using namespace std;
int w[MAX_N],v[MAX_N];
int dp[MAX_N + 1][MAX_M+ + 1]; //dp[i][j] 前i-1个物体中挑选重量不超过j时的最大价值
//其中,dp[0][j]=0;
//特征方程 dp[i][j] = dp[i-1][j]; j<w[i]
// max(dp[i-1][j], dp[i-1][j=w[i]] + v[i]);
int main()
{
int n,W;
cin>>n>>W;
if(n < 1 || n > 10000 || W < 1 || W > 1000)
return 0;
for(int i = 1; i <= n; i++)
{
cin>>w[i]>>v[i];
if( w[i] < 1 || w[i] > 10000 || v[i] < 1 || v[i] > 100)
return 0;
}
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= W; j++)
{
if(j < w[i])
dp[i][j] = dp[i-1][j];
else
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
}
}
cout<<dp[n][W]<<endl;
return 0;
}
完全背包
完全背包就是每种物品有无限个的背包
这个时候我们的状态转移方程可以这么写:dp[i][j]=max(dp[i-1][j],dp[i][j-v[i]]+w[i]);
看起来好像没区别啊?!
真的么?
仔细看看,i-1变成了i
也就是说,选择了第i个物品后,并不是转到i-1,因为每个物品有无限个,所以还是转到了i
状压一下,就是
dp[j]=max(dp[j],dp[j-v[i]]+w[i]) 【j=v[i]->N】
唯一的区别就是j枚举的方向反了过来
for(int i=1;i<=N;i++)
for(int j=v[i];j<=M;j++)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
自写代码 二维记忆数组
#include <iostream>
#include<cstdio>
#define MAX_N 10001
#define MAX_M 1001
using namespace std;
int w[MAX_N],v[MAX_N];
int dp[MAX_N + 1][MAX_M+ + 1];//dp[i][j] 前i-1种物体中挑选重量不超过j时的最大价值
//其中,dp[0][j]=0;
//特征方程 dp[i][j] = dp[i-1][j]; j<w[i]
// max(dp[i-1][j], dp[i][j-w[i]] + v[i]);
int main()
{
int n,W;
cin>>n>>W;
if(n < 1 || n > 10000 || W < 1 || W > 1000)
return 0;
for(int i = 1; i <= n; i++)
{
cin>>w[i]>>v[i];
if( w[i] < 1 || w[i] > 10000 || v[i] < 1 || v[i] > 100)
return 0;
}
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= W; j++)
{
if(j < w[i])
dp[i][j] = dp[i-1][j];
else
dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]]+v[i]); //后者如果选择加入第i个物体则可以重复多次选择第i个物体
}
}
cout<<dp[n][W]<<endl;
return 0;
}