AcWing 6. 多重背包问题 III 背包问题的第一大难关

AcWing 6. 多重背包问题 III
有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。

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

输入格式
第一行两个整数,N,V (0<N≤1000, 0<V≤20000),用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

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

数据范围
0<N≤1000
0<V≤20000
0<vi,wi,si≤20000
提示
本题考查多重背包的单调队列优化方法。

输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10

这道题是一个多重背包问题,上一次我们求多重背包问题的方法是转化成完全背包问题去做,这个方法在这道题不适用,这个时候我们应该介绍一个新的方法。
先说一下完全背包和多重背包的差别:

在这里插入图片描述
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − v ] + w , d p [ i − 1 ] [ j − 2 ∗ v ] + 2 ∗ w , . . . , d p [ i − 1 ] [ j − k ∗ v ] + k ∗ w ) dp[i][j] = max(dp[i-1][j], dp[i-1][j-v] + w, dp[i-1][j-2*v] + 2*w,..., dp[i-1][j-k*v] + k*w) dp[i][j]=max(dp[i1][j],dp[i1][jv]+w,dp[i1][j2v]+2w,...,dp[i1][jkv]+kw)
d p [ i ] [ j − v ] = m a x ( d p [ i − 1 ] [ j − v ] + w , d p [ i − 1 ] [ j − 2 ∗ v ] + 2 ∗ w , . . . , d p [ i − 1 ] [ j − k ∗ v ] + k ∗ w ) dp[i][j-v] = max( dp[i-1][j-v] + w, dp[i-1][j-2*v] + 2*w,..., dp[i-1][j-k*v] + k*w) dp[i][jv]=max(dp[i1][jv]+w,dp[i1][j2v]+2w,...,dp[i1][jkv]+kw)

根据上面两个式子可以得出,其实完全背包问题是前面 d p [ i ] [ j − v ] dp[i][j-v] dp[i][jv]前缀 的最大值。

但是多重背包问题就有一点点区别了。

d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − v ] + w , dp[i][j] = max(dp[i-1][j], dp[i-1][j-v] + w, dp[i][j]=max(dp[i1][j],dp[i1][jv]+w, d p [ i − 1 ] [ j − 2 ∗ v ] + 2 ∗ w , . . . , d p [ i − 1 ] [ j − s ∗ v ] + s dp[i-1][j-2*v] + 2*w,..., dp[i-1][j-s*v] + s dp[i1][j2v]+2w,...,dp[i1][jsv]+s ∗ w ) *w) w)

d p [ i ] [ j − v ] = m a x ( d p [ i − 1 ] [ j − v ] , d p [ i − 1 ] [ j − 2 ∗ v dp[i][j-v]=max(dp[i-1][j-v],dp[i-1][j-2*v dp[i][jv]=max(dp[i1][jv],dp[i1][j2v ] , … … , d p [ i − 1 ] [ j − s ∗ v ] + ( s − 1 ) ∗ v , d p [ i − ],……,dp[i-1][j-s*v]+(s-1)*v,dp[i- ],,dp[i1][jsv]+(s1)v,dp[i 1 ] [ j − ( s + 1 ) ∗ v + s ∗ v ) 1][j-(s+1)*v+s*v) 1][j(s+1)v+sv)

看式子可能无法直观 的去看到滑动窗口。

我们手写一下式子。
在这里插入图片描述
由此可见,这个是在长度为sv 的滑动窗口,可能我们求的范围小于sv,但是也满足滑动窗口的条件,我们就用滑动窗口的方式去求这个多重背包问题。我们要实现对所有体积进行更新,所以我们开始的起点在 0-v-1 (余)上,这样实现对所有元素的更新。

所以,我们可以得到
dp[j]    =     dp[j]
dp[j+v]  = max(dp[j] +  w,  dp[j+v])
dp[j+2v] = max(dp[j] + 2w,  dp[j+v] +  w, dp[j+2v])
dp[j+3v] = max(dp[j] + 3w,  dp[j+v] + 2w, dp[j+2v] + w, dp[j+3v])
...
但是,这个队列中前面的数,每次都会增加一个 w ,所以我们需要做一些转换
...

我们在处理的时候会遇到w怎么分配的问题,
y总 给出了很好的解决方案

dp[j]    =     dp[j]
dp[j+v]  = max(dp[j], dp[j+v] - w) + w
dp[j+2v] = max(dp[j], dp[j+v] - w, dp[j+2v] - 2w) + 2w
dp[j+3v] = max(dp[j], dp[j+v] - w, dp[j+2v] - 2w, dp[j+3v] - 3w) + 3w
...
这样,每次入队的值是 dp[j+k*v] - k*w

以上代码参考于链接
最后代码如下:

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010,M=20010;

int f[M];
int g[M];
int q[M];
int n,m;
int hh,tt;
int main(void)
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int v,w,s;
        cin>>v>>w>>s;
        memcpy(g,f,sizeof f);
        for(int j=0;j<v;j++)
        {
            hh=0;tt=-1;
            for(int k=j;k<=m;k+=v)
            {
                if(hh<=tt&&q[hh]<k-s*v) hh++;
                while(hh<=tt&&g[q[tt]]-(q[tt]-j)/v*w<=g[k]-(k-j)/v*w) tt--;
                q[++tt]=k;
                f[k]=g[q[hh]]+(k-q[hh])/v*w;
            }
        }
    }
    cout<<f[m];
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值