动态规划--背包

懵懂的女人 提交于 2019-12-26 22:35:34

1. 01背包:有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 c[i],价值是 w[i]。求解将哪些物品装入背包可使价值总和最大。

  对于这类问题我们我们定义f[i][j]表示在前i个物品中选总容量为j所能得到的最大价值为多少于是我们状态转移便是这样 f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);

int f[N][M];
void work()
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
        for(int j=w[i];j<=m;j++)
            f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);
    for(int i=1;i<=m;i++) ans=max(f[n][i],ans);
    printf("%d\n",ans);
}

  这样一来,我们的时间复杂度就是O(nm),空间复杂度为O(nm),但我们发现f[i]的值只与f[i-1]有关,此时我们可以用滚动数组来优化空间到O(m),为f[j]=max(f[j],[j-w[i]]+v[i]);此时我们的j就要倒序枚举,因为j只会从比它小的j那转移。

int f[M];
void work()
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
        for(int j=m;j>=w[i];j--)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    for(int i=1;i<=m;i++) ans=max(f[i],ans);
    printf("%d\n",ans);
}

2.完全背包问题:有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用。第 i 种物品的费用是 c[i],价值是 w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。这个问题非常类似于 01 背包问题,所不同的是每种物品有无限个。

  这时我们就有一个思路,我们将01背包倒序的滚动数组做法正序,这样我们就起到了一个物品选多次的效果

int f[M];
void work()
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
        for(int j=w[i];j<=m;j++)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    for(int i=1;i<=m;i++) ans=max(f[i],ans);
    printf("%d\n",ans);
}

3.多重背包问题:有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 n[i]件可用,每件费用是 c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

若是我们将东西直接拆成一个一个,再跑01背包,这势必会超时,这时我们要用到2进制的思想,因为任何一个数都可以拆成由1,2,4,8..因为他们的二进制是01的

int f[M];
void work()
{
    memset(f,0,sizeof(f));
    for(int i=n;i>=1;i--)
    {
        --g[i];
        for(int b=2;b<=g[i];b<<=1)
        {
            g[i]-=b;
            w[++n]=w[i]*b;
            v[n]=v[i]*b;
        }
        if(g[i]!=0)
        {
            w[++n]=w[i]*g[i];
            v[n]=v[i]*g[i];
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=m;j>=w[i];j--)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    for(int i=1;i<=m;i++) ans=max(f[i],ans);
    printf("%d\n",ans);
}

4.混合三种背包问题:如果将三种背包问题混合起来。也就是说,有的物品只可以取一次(01 背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。应该怎么求解呢?

01 背包与完全背包的混合考虑只有一处不同,故如果只有两类物品:一类物品只能取一次,另一类物品可以取无限次,那么只需在对每个物品应用转移方程时,根据物品的类别选用顺序或逆序的循环即可,复杂度是 O(nm)。

int f[M];
void work()
{
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
        if(g[i]) for(int j=w[i];j<=m;j++)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
        else for(int j=m;j>=w[i];j--)
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    for(int i=1;i<=m;i++) ans=max(f[i],ans);
    printf("%d\n",ans);
}

如果是再加上多重背包,再加上有的物品最多可以取有限次,我们就只能拆分物品

5.二维费用的背包问题:二维费用的背包问题是指:对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。

  费用加了一维,只需状态也加一维即可。设 f[i][v][u]表示前 i 件物品付出两种代价分别为 v和 u 时可获得的最大价值。状态转移方程就是:f[i][v][u]=max(f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i])

6.分组的背包问题:有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 c[i],价值是 w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

  这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设f[k][v]表示前 k 组物品花费费用 v 能取得的最大权值,则有:f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于第k 。

int f[M];
void work()
{
    memset(f,0,sizeof(f));
    for(int k=1;k<=n;k++)
        for(int i=1;i<=w[k][0];i++)
            for(int j=m;j>=w[i];j--)
                f[j]=max(f[j],f[j-w[k][i]]+v[k][i]);
    for(int i=1;i<=m;i++) ans=max(f[i],ans);
    printf("%d\n",ans);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!