01背包问题

01背包问题

有 N件物品和一个容量是 V的背包。每件物品只能使用一次。

第 i件物品的体积是 v[i],价值是 w[i]。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 v[i],w[i],用空格隔开,分别表示第 i件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

输入样例:

4 5 
1 2 
2 4 
3 4 
4 5

输出样例:

8

首先要明确状态转移方程

dp[i][j]表示从前i个物品中选择,使得总体积不超过j的最大价值

我们要考虑如何将目前的状态用前面已经计算过的状态来更新

对于第i个物品,有两种状态:选与不选

如果不选第i个物品,那么dp[i][j] = dp[i-1][j]

如果选择第i个物品, 那么dp[i][j] = dp[i-1][j-v[i]] + w[i] (要判断if(j>v[i]))

#include <iostream>
using namespace std;
const int N = 1010;
int v[N],w[N];
int n,V;
int dp[N][N];  //dp[i][j]表示从前i个物品中选择,总体积不超过j时,最大的价值
int main()
{
    cin>>n>>V;
    for(int i=1 ; i<=n ; i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1 ; i<=n ; i++)
        for(int j=0 ; j<=V ; j++)
        {
            dp[i][j] = dp[i-1][j]; //不选第i个物品
            if(j>=v[i]) dp[i][j] = max(dp[i][j] , dp[i-1][j-v[i]]+w[i]); 
        }
    cout<<dp[n][V];
    return 0;
}

在上面的代码的第17、18行中我们发现 i 的状态只和 i-1 的状态有关,可以考虑将数组优化为一维

考虑一下这个循环,我们需要将它优化为一维

 
for(int i=1 ; i<=n ; i++)
        for(int j=0 ; j<=V ; j++)
        {
            dp[i][j] = dp[i-1][j]; //不选第i个物品
            if(j>=v[i]) dp[i][j] = max(dp[i][j] , dp[i-1][j-v[i]]+w[i]); 
        }

那我们直接将第一维删掉会怎样?

for(int i=1 ; i<=n ; i++)
        for(int j=0 ; j<=V ; j++)
        {
            if(j>=v[i]) dp[j] = max(dp[j] , dp[j-v[i]]+w[i]); 
        }

当我们枚举到 i 的时候,会枚举所有的 0<=j<=V , 也就是数组dp[i][j] , 此时i固定,那就只考虑j, 也就是dp[j]

当枚举到 i 的时候,dp[j] (j从0到V),存的是什么呢, 是枚举i-1时计算的值, 也就是dp[i-1][j]

那么上面的第二层循环就有问题了

计算dp[j]的时候需要用到dp[j-v[i]],在二维的时候是计算dp[i][j]需要用到dp[i-1][j-v[i]]

每次枚举j,就意味着dp[j]被更新为了从前 i 个物品中选择,也就是dp[i][j].

如果我们从小到大枚举j,那么前面的dp[j]就被更新了, 而后面的dp[j]是需要用i-1状态下的dp[j-v[i]]更新的, 此时的dp[j-v[i]]被更新为了i状态下,因此j的枚举应该从大到小.

#include <iostream>
using namespace std;
const int N = 1010;
int v[N],w[N];
int n,V;
int dp[N];  //dp[j]在每次枚举i时,表示从前i个物品中选择,总体积不超过j时,最大的价值
int main()
{
    cin>>n>>V;
    for(int i=1 ; i<=n ; i++)
    {
        cin>>v[i]>>w[i];
    }
    for(int i=1 ; i<=n ; i++)
        for(int j=V ; j>=0 ; j--)
        {
            if(j>=v[i]) dp[j] = max(dp[j] , dp[j-v[i]]+w[i]); 
        }
    cout<<dp[V];
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值