处理何种问题:给定n种物品和一个容量为V的背包,物品i的体积为vi,其价值为pi,求其最终可以装进背包的物品最大价值。
性能:时间复杂度为O(nV)
原理:
- 首先明确一点:对于先放那个物品对最后结果无影响
- 设 dp[i][j] 为有前i个物品,且容量为j时背包所能得到的最大价值(暂时不要问是怎么得出dp[i][j]值的),则当出现第(i+1)个物品时,dp[i+1][j]=max(dp[i][j] ,dp[i][j-arr[i+1].v]+arr[i+1].p),即比较在不装这个物品的情况下的背包最大价值和装了这个物品下的最大价值的大小,进行取舍。
- 只要将dp[0][j] 都设置为0,其状态转移方程就也适用1~n个物品。
实现步骤:对于该状态转移方程的二维数组实现方式我就不在这里说了。
一维数组的实现方式:因为该方程式其实每次只需要考虑第i个和第i-1个,可以用2个一维数组去滚动求解,也可以只用一个一维数组进行求解,坑点在于为了保证(j-arr[i].v) 访问的是第i-1个物品的数据,j的遍历顺序必须是(V~arr[i].v)否则会提前修改数据,导致后面的不可用。
备注:dp求解和递归求解在思考模式上还是有很大不同的。
标准的状态转移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-arr[i].v]+arr[i].p);
输入样例解释:
5 1000//5种物品,背包容量为1000
144 990 //第1种物品的体积、价值
487 436
210 673
567 58
1056 897
输出样例解释:
2099//最大价值
#include<iostream> #include<cstdio> #include<string.h> using namespace std; const int MaxN=100010; struct node { int p,v; }; node arr[MaxN]; int dp[MaxN]; int N,V; int main() { scanf("%d%d",&N,&V); for(int i=1;i<=N;++i) scanf("%d%d",&arr[i].v,&arr[i].p); memset(dp,0,sizeof(dp)); for(int i=1;i<=N;++i) { for(int j=V;j>=arr[i].v;--j) { dp[j]=max(dp[j],dp[j-arr[i].v]+arr[i].p); } } printf("%d ",dp[V]); return 0; }