hdu 2191(单调队列优化多重背包模板)

题目链接

裸的多重背包,数据也很水,只是打个模板用的

一般的多重背包时间复杂度 O(VN) O ( V ∑ N ) ,二进制优化后 O(VlogN) O ( V ∑ l o g N ) ,但有的题必须 O(NV) O ( N ⋅ V ) 才能过,这就必须用单调队列优化了

设物品 i i 体积v[i],价值 w[i] w [ i ] ,个数 n[i] n [ i ]

多重背包dp方程:

dp[i][j]=max{dp[i1][jkv[i]]+kw[i]}(0kmin(n[i],j/v[i])) d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j − k ⋅ v [ i ] ] + k ⋅ w [ i ] } ( 0 ≤ k ≤ m i n ( n [ i ] , j / v [ i ] ) )

a=j/v[i] a = j / v [ i ] b=j%v[i] b = j % v [ i ] ,即 j=av[i]+b j = a ⋅ v [ i ] + b

在每一轮循环中 i i 相同,即v w w n都相同

dp方程变形为:

dp[i][av+b]=max{dp[i1][(ak)v+b]+kw}(0kmin(n,a)) d p [ i ] [ a ⋅ v + b ] = m a x { d p [ i − 1 ] [ ( a − k ) ⋅ v + b ] + k ⋅ w } ( 0 ≤ k ≤ m i n ( n , a ) )

s=ak s = a − k ,那么 amin(n,a)smin(n,a) a − m i n ( n , a ) ≤ s ≤ m i n ( n , a )

代入dp方程得:

dp[i][av+b]=max{dp[i1][sv+b]sw}+aw(amin(n,a)smin(n,a)) d p [ i ] [ a ⋅ v + b ] = m a x { d p [ i − 1 ] [ s ⋅ v + b ] − s ⋅ w } + a ⋅ w ( a − m i n ( n , a ) ≤ s ≤ m i n ( n , a ) )

f[x]=dp[i][xv+b] f [ x ] = d p [ i ] [ x ⋅ v + b ] g[x]=dp[i1][xv+b]xw g [ x ] = d p [ i − 1 ] [ x ⋅ v + b ] − x ⋅ w h[x]=xw h [ x ] = x ⋅ w ,那么上式即为:

f[a]=max{g[s]}+h[a](amin(n,a)smin(n,a)) f [ a ] = m a x { g [ s ] } + h [ a ] ( a − m i n ( n , a ) ≤ s ≤ m i n ( n , a ) )

用单调队列维护 g[s] g [ s ] 即可实现 O(1) O ( 1 ) 转移,要注意满足 s s <script type="math/tex" id="MathJax-Element-28">s</script>的范围

具体实现见代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=110;
int C,N,M,dp[maxn];
int Q[maxn][2],head,tail;//Q[x][0]存g[s],Q[x][1]存s以判断s是否符合限制条件

int main(){
    scanf("%d",&C);
    while(C--){
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&N,&M);
        for(int i=1;i<=M;i++){
            int v,w,n;
            scanf("%d%d%d",&v,&w,&n);
            for(int b=0;b<v;b++){//枚举余数b
                head=tail=1;//清空队列,加在这里是因为只能从余数相同的状态转移来,之前队列中的元素都不需要了
                for(int j=0;j<=(N-b)/v;j++){
                    int tmp=dp[j*v+b]-j*w;//这里的dp是上一轮的,j代表s
                    while(head<tail&&tmp>=Q[tail-1][0]) --tail;//先插入以满足s≤min(n,a)的条件
                    Q[tail][0]=tmp,Q[tail++][1]=j;//这里j还是代表s
                    while(Q[head][1]<j-min(n,j)) ++head;//以下j代表a,删除队首以去除不满足a−min(n,a)≤s的状态
                    dp[j*v+b]=Q[head][0]+j*w;//队首即为最优
                }
            }
        }
        printf("%d\n",dp[N]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值