01背包

01背包

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,价值是 wi,求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
思路
每个物品只有一件,考虑拿与不拿,在前i件物品中,拿第i件物品与不拿第i件物品,其限制条件是背包的空间,那么状态可表示为dp[前i件物品][背包容量],当前状态依赖于之前的状态,考虑前i件物品拿与不拿,状态转移方程可表示为,dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]),不拿第i件物品,那么就是在当前体积为j的情况下去拿,拿了第i件物品,那么就从前i-1件物品中,在j-v[i]的体积去拿。这样可以枚举出所有情况。
朴素写法

#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimization ("unroll-loops")
using namespace std;
#define ll long long
#define sl(n) scanf("%lld",&n)
#define pl(n) printf("%lld",n)
#define sdf(n) scanf("%lf",&n)
#define pdf(n) printf("%.lf",n)
#define pE printf("\n")
#define ull unsigned long long
#define pb push_back
#define pre(n) for(ll i=1;i<=n;i++)
#define rep(n) for(ll i=n;i>=1;i--)
#define ph push
#define pi pair<ll,ll>
#define fi first
#define se second
ll n, v, volume[1010], value[1010];
ll dp[1010][1010];
int main()
{
    cin >> n >> v;
    pre(n)cin >> volume[i] >> value[i];
    for (ll  i = 1; i <= n; i++)
    {
        for (ll  j = 1; j <= v; j++)
        {
            dp[i][j] = dp[i - 1][j];
            if (j >= volume[i])dp[i][j] = max(dp[i][j], dp[i - 1][j - volume[i] ]+ value[i]);
            }
    }
    cout << dp[n][v];
    return 0;
}

优化空间

dp[i][j] = dp[i - 1][j];
if (j >= volume[i])dp[i][j] = max(dp[i][j], dp[i - 1][j - volume[i] ]+ value[i]);

这里的i在内层循环中一直没有发生过变化,那么可以优化到一维dp,二维dp是dp[i][j]表示前i件物品的最大价值,那么优化到一维状态只表示背包的容量,那么对原式做等价变形,if (j >= volume[i])只有当前背包体积大于等于当前物品体积时,才能放进去并进行比较,状态才会发生变化,那么你从大到小进行遍历

for (ll  i = 1; i <= n; i++)
    {
        for (ll  j = v; j >= volume[i] ; j--)dp[j] = max(dp[j], dp[j - volume[i]] + value[i]);
    }

为什么大到小遍历是正确的呢,因为当你更新dp[j]时,它的每一次状态都是从i-1的状态,和原式是等价的

#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimization ("unroll-loops")
using namespace std;
#define ll long long
#define sl(n) scanf("%lld",&n)
#define pl(n) printf("%lld",n)
#define sdf(n) scanf("%lf",&n)
#define pdf(n) printf("%.lf",n)
#define pE printf("\n")
#define ull unsigned long long
#define pb push_back
#define pre(n) for(ll i=1;i<=n;i++)
#define rep(n) for(ll i=n;i>=1;i--)
#define ph push
#define pi pair<ll,ll>
#define fi first
#define se second
ll n, v, volume[1010], value[1010];
ll dp[1010];
int main()
{
    cin >> n >> v;
    pre(n)cin >> volume[i] >> value[i];
    for (ll  i = 1; i <= n; i++)
    {
        for (ll  j = v; j >= volume[i] ; j--)dp[j] = max(dp[j], dp[j - volume[i]] + value[i]);
    }
    cout << dp[v];
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值