[DP]多重背包

多重背包

问题描述:给定nnn种物品和一个体积为VVV的背包,第iii种物品数量为mim_imi,体积为cic_ici,价值为wiw_iwi。如何装填背包使总价值最大?

通过直接求解,转移方程式:dp[i][j]=max⁡(dp[i−1][j],dp[i−1][j−k×c[i]]+k×w[i]),k∈[1,min⁡(m[i],jc[i])]dp[i][j]=\max(dp[i-1][j],dp[i-1][j-k\times c[i]]+k\times w[i]),k\in[1,\min(m[i],\frac{j}{c[i]})]dp[i][j]=max(dp[i1][j],dp[i1][jk×c[i]]+k×w[i]),k[1,min(m[i],c[i]j)]。复杂度O(V∑i=1nmi)O(V\sum\limits_{i=1}^n m_i)O(Vi=1nmi),超时。

实际上,多重背包属于0/10/10/1背包的推广,易得其可转换为0/10/10/1背包问题:将第iii种物品视为mim_imi种独立(不同)的物品,并按0/10/10/1背包求解。定义状态数组dp[i][j]dp[i][j]dp[i][j],表示将前iii个物品放入容积为jjj的背包时的最大价值。实际上复杂度不变,仍为O(V∑i=1nmi)O(V\sum\limits_{i=1}^n m_i)O(Vi=1nmi),超时。

int dp[n+1][V+1],c[n],w[n],m[n];
int MultiplePack(){
    for(int i=0;i<=n;i++) dp[i][0]=0;
    for(int i=0;i<=V;i++) dp[0][i]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=V;j++)
            for(int k=1;k<=m[i]&&k*c[i]<=j;k++)
                dp[i][j]=max(dp[i][j],dp[i-1][j-k*c[i]]+k*w[i]);
    return dp[n][V];
}

二进制优化

原理:倍增。任意十进制整数均可使用2的幂次经过有限次相加得到,以2i(i∈[0,⌈log⁡2mi⌉+1])2^i(i\in[0,\lceil\log_2m_i\rceil+1])2i(i[0,log2mi+1])顺次拆分,最后可能有一个余数。因此使用倍增即可将第iii种物品变为log⁡2mi\log_2m_ilog2mi个,每个物品体积为2k×ci2^k\times c_i2k×ci,价值为2k×wi2^k\times w_i2k×wi

以下为二进制拆分代码,之后使用new_nnew_cnew_w0/10/10/1背包求解即可。复杂度O(V∑i=1nlog⁡2mi)O(V\sum\limits_{i=1}^n \log_2m_i)O(Vi=1nlog2mi)

int new_n=0,new_c[N],new_w[N];
for(int i=1;i<=n;i++){//遍历每种物品
    for(int j=1;j<=m[i];j<<=1){//遍历每种物品的个数
        new_n++;
        m[i]-=j;
        new_c[new_n]=j*c[i];
        new_w[new_n]=j*w[i];
    }
    if(m[i]){//若有余数
        new_n++;
        new_c[new_n]=m[i]*c[i];
        new_w[new_n]=m[i]*w[i];
    }
}

单调队列优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值