多重背包

多重背包

问题描述:给定\(n\)种物品和一个体积为\(V\)的背包,第\(i\)种物品数量为\(m_i\),体积为\(c_i\),价值为\(w_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]})]\)。复杂度\(O(V\sum\limits_{i=1}^n m_i)\),超时。

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

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的幂次经过有限次相加得到,以\(2^i(i\in[0,\lceil\log_2m_i\rceil+1])\)顺次拆分,最后可能有一个余数。因此使用倍增即可将第\(i\)种物品变为\(\log_2m_i\)个,每个物品体积为\(2^k\times c_i\),价值为\(2^k\times w_i\)

以下为二进制拆分代码,之后使用new_nnew_cnew_w\(0/1\)背包求解即可。复杂度\(O(V\sum\limits_{i=1}^n \log_2m_i)\)

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];
    }
}

单调队列优化

posted @ 2025-04-05 10:45  椰萝Yerosius  阅读(34)  评论(0)    收藏  举报