背包问题——0-1背包

有N件物品和一个最大承重为W的背包,第i件物品的重量为w[i],价值是v[i],每种物品仅有一件,可以选择放或不放,求解将哪些物品放入背包使得总重量不超过W且获得的总价值最大。

分析

这是一类最基础的背包问题,特点是每种物品只有一个。
一、状态
f[i][j]:表示前i件物品重量不超过j的最大价值
w[i]:第i件物品的重量
v[i]:第i件物品的价值

二、状态转移方程式
f[i][j]这个子问题“将前i件物品放入重量为j的背包中”,假设前面的第i-1件物品已经放好,在f[i-1][j]中,只考虑第i件物品的决策,那么就可以转化为只与前i-1件物品有关的问题。如果不放,那么问题就转化为“前i-1件物品放入重量为j的背包中”;如果放,那么问题就转化为“前i-1件物品放入重量为j-w[i]的背包中”(w[i]为当前背包重量,因为占了这个背包重量,所以要减,不是加),此时获得的价值就应该是f[i-1][j-w[i]]再加上v[i]。
所以推出状态转移方程式为:
f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
1<=i<=n,w[i]<=j<=W

三、初值
这个初值故意留到状态转移方程式之后来说。我们来仔细看看这个状态转移方程式,为了使f[i-1][j-w[i]]有意义,j必须大于w[i]。那这样的话,当j

#include<cstdio>
#include<algorithm>
using namespace std;
int n,W,f[105][1005],w[105],v[105],ans;
int main()
{
    scanf("%d %d",&n,&W);
    for(int i=1;i<=n;i++)
        scanf("%d %d",&w[i],&v[i]);
    for(int i=1;i<=n;i++)
        for(int j=0;j<=W;j++)
            if(w[i]<=j)
                f[i][j]=max(f[i-1][j-w[i]]+v[i],f[i-1][j]);
            else f[i][j]=f[i-1][j];
    for(int i=1;i<=W;i++)
        ans=max(ans,f[n][i]);
    printf("%d\n",ans);
}

空间优化

时间复杂度为O(N*W),已经基本上不能优化了,但是空间复杂度还可以优化,我们可以使用一个滚动一维数组f[j],就相当于原来的f[i][j],表示重量不大于j的最大价值。注意第二重for循环要逆序。原因:原来的f[i][j]是由f[i-1][j]和f[i-1][j-w[i]]推来的,要保证f[j]是原来f[i-1][j]和f[i-1][j-w[i]]的值,就只能逆序,因为滚动数组会覆盖掉原来的值,如果顺序,f[j]就是由f[i][j-w[i]]推来的了。
如果大家还不是很明白,可以用数据推一下看看。

猜你喜欢

转载自blog.csdn.net/cqbzlytina/article/details/77521979