在0-1背包问题中,我们设 表示在考虑了前i个物品后,大小为V的背包的最大价值, 表示第i件物品的重量, 表示第i件物品的价值,其状态转移方程为
如果我们把 看做关于背包容积V的函数列,则很容易发现 只与 , , 相关,即 可以由 递推得来。
既然i-1以前的F都用不到,我们就可以使用滚动数组了,即用 与进行递推,只占用两个F的空间。
但是,这样的优化也还是不够的。
想象一下,如果我们只使用一个数组F会造成什么后果?很明显会使得的部分值被覆盖,这没问题。但巧就巧在, 所依赖的项 与 的下标都小于或等于V,也就是说( >=) 被覆盖了并不会影响的计算。
所以,状态转移方程变成了
并且V是倒着推的。
另附彩蛋:上面这个方程的V正着推可以解决完全背包问题。
#include<cstdio> #include<cmath> #include<cstring> #include<iostream> #include<cstdlib> #include<algorithm> #include<string> #include<stack> #include<queue> #include<map> #include<stdio.h> #include<stdlib.h> using namespace std; const int maxn=100;//物品最大上线 const int maxv=1000;//v的上线 int w[maxn],c[maxn],dp[maxv]; int n,V; int main(){ scanf("%d%d",&n,&V); for(int i=1;i<=n;i++){ scanf("%d",&w[i]); } for(int i=1;i<=n;i++){ scanf("%d",&c[i]); } for(int i=1;i<=n;i++){ for(int v=V;v>=w[i];v--){ dp[v]=max(dp[v],dp[v-w[i]]+c[i]); } } int ans=0; for(int i=0;i<=V;i++){ if(ans<dp[i]) ans=dp[i]; } printf("%d\n",ans); return 0; }