多重背包优化

多重背包优化

二进制拆分

18个物品可以拆成1,2,4,8,3倍物品,这样5个物品任意取可以得到1~18个物品的所有方案。

时间复杂度是\(O(nWlogP)\),P是一种物品的最大数量

#include <bits/stdc++.h>
using namespace std;
const int maxw=4e4+5;
const int maxn=1e4+5;
int dp[2][maxw];
int v[maxn],w[maxn];
int main () {
    int n,W;
    scanf("%d%d",&n,&W);
    int cnt=0;
    for(int i=1;i<=n;i++){
        int v1,w1,m1;
        scanf("%d%d%d",&v1,&w1,&m1);
        int j=1;
        while((1<<j)-1<m1){
            v[++cnt]=v1*(1<<(j-1));
            w[cnt]=w1*(1<<(j-1));
            j++;
        }
        j--;
        int rest=m1-((1<<j)-1);
        v[++cnt]=rest*v1;
        w[cnt]=rest*w1;
    }
    for(int i=1;i<=cnt;i++){
        int cur=i&1;
        for(int j=0;j<=W;j++)dp[cur][j]=dp[cur^1][j];
        for(int j=w[i];j<=W;j++){
            dp[cur][j]=max(dp[cur][j],dp[cur^1][j-w[i]]+v[i]);
        }
    }
    int ans=0;
    for(int j=0;j<=W;j++)ans=max(ans,dp[cnt&1][j]);
    printf("%d\n",ans);
}

单调队列

w:重量,v:价值,可以得到一般转移方程

\[f[j]=max(f[j−w∗k]+v∗k);(k<=c) \]

\(d=j\bmod w\),\(s=j/w\)

\[f[j]=max(f[d+w∗k]−v∗k)+v∗s(s-k<=c) \]

可以发现对j按模w的余数划分剩余类,dp的转移只会发生在每一个剩余类之内。因此可以枚举模数d,用单调队列维护一个大小为k的窗口的\(f[d+w∗k]−v∗k\)的最大值,这样每次转移都是O(1)的,总的背包的时间复杂度是O(n*W)

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e2+5;
const int maxw=4e4+5;
int dp[maxw];
int v[maxn],w[maxn],m[maxn];
int main () {
    int n,W;
    scanf("%d%d",&n,&W);
    int ans=0;
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&v[i],&w[i],&m[i]);
        if(w[i]==0)ans+=v[i]*m[i];
        else m[i]=min(m[i],W/w[i]);
    }
    for(int i=1;i<=n;i++){
        if(w[i]==0)continue;
        for(int d=0;d<w[i];d++){//枚举余数
            int k=(W-d)/w[i];//状态数
            vector<int>qi(k+2),qv(k+2);//idx,value
            int l=1,r=0;
            for(int j=0;j<=k;j++){
                while(r>=l&&dp[d+j*w[i]]-j*v[i]>=qv[r])r--;
                qv[++r]=dp[d+j*w[i]]-j*v[i];
                qi[r]=j;
                while(r>=l&&qi[l]<j-m[i])l++;
                dp[d+j*w[i]]=max(dp[d+j*w[i]],qv[l]+j*v[i]);
            }
        }
    }
    int maxx=0;
    for(int i=0;i<=W;i++)maxx=max(maxx,dp[i]);
    ans+=maxx;
    printf("%d\n",ans);
}
posted @ 2021-03-26 16:58  UCPRER  阅读(77)  评论(0编辑  收藏  举报