在0-1背包问题中,我们设F_i[V] 表示在考虑了前i个物品后,大小为V的背包的最大价值,w_i 表示第i件物品的重量,v_i 表示第i件物品的价值,其状态转移方程为

F_i[V]=max\{F_{i-1}[V],F_{i-1}[V-w_i]+v_i\} (V-w_i>=0,F_0[v]=0)

如果我们把F_i 看做关于背包容积V的函数列,则很容易发现F_i 只与F_{i-1} ,w_i ,v_i 相关,即F_i 可以由F_{i-1} 递推得来。

既然i-1以前的F都用不到,我们就可以使用滚动数组了,即用F_{i \% 2}F_{(i-1) \% 2}进行递推,只占用两个F的空间。

但是,这样的优化也还是不够的。

想象一下,如果我们只使用一个数组F会造成什么后果?很明显会使得F_{i-1}的部分值被覆盖,这没问题。但巧就巧在,F_i[V] 所依赖的项F_{i-1}[V]F_{i-1}[V-w_i] 的下标都小于或等于V,也就是说F_{i-1}[v](v >=V) 被覆盖了并不会影响F_i[V]的计算。

所以,状态转移方程变成了

F[V]=max\{F[V],F[V-w_i]]+v_i\}

并且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;
}

 



posted on 2018-04-12 09:32  Sunshine&暖阳  阅读(108)  评论(0编辑  收藏  举报