背包九讲整理

01背包

题目

有N件物品和一个容量为V的背包。放放入第 i 件物品耗费的费用是C1,得到的价值是W1,求放入哪些物品能使获得价值最大。
最基础的背包问题,叫做01是因为每件物品只有一种情况--拿或不拿。由此设置一个数组F,F[i,v]表示前 i 件物品恰放入一个容量为 v 的背包可以获得的最大价值。从而得到方程

F[i,v]=max(F[i-1,v],F[i - 1,v-Ci]+Wi)

这个式子代表了整个背包问题的思想,其含义就是只考虑当前物品i拿或者不拿,将拿了物品i后目前总价值(F[i - 1,v-Ci]+Wi)与不拿物品i的情况F[i-1,v]比较,若是F[i-1,v]>F[i - 1,v-Ci]+Wi,就代表之前记录中有更好的方案,因此要放弃i。反之则将i放入背包,并更新F中最大值。
该方程可以优化为一维数组表示

#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
int main()
{
    int w[5] = {0, 2, 3, 4, 5};//weight
    int v[5] = {0, 3, 4, 5, 6};//value
    int bagv = 8;//bag's space
    int dp[9] = {0};
    for (int j = 1; j <= 4; j++)
        for (int i = bagv; i >= w[j]; i--) //如果01背包就是i=bagv->w[j],完全背包(不限量拿取)为i=w[j]->bagv
        {
            dp[i] = max(dp[i], dp[i - w[j]] + v[j]);
        }
    cout<<dp[bagv];
}

多重背包

题目

有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 Mi 件可用,每件耗费的
空间是 Ci,价值是 Wi。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。

多重背包相对于完全背包大体相同,不同在于每件物品有了数量限制,因此在完全背包问题上做出修改即可。

F[i,v] = max(F[ i-1 , v ],F[i - 1,v - k - Ci] + k - Wi | 0 ≤ k ≤ Mi)

复杂度是 O(V ΣMi),但我们能将其转换为01背包,降低复杂度至O(V ΣlogMi)。
具体思路是通过二进制的思想,将第i种物品分成若干件01背包式物品,这样这件物品费用与价值均是原来费用与价值乘这个系数。
比如一件物品有15,可以分解为系数为1(2^0),2,4,8的物品,这样1~15之间任意整数都可以由这四个系数构成。接着分别用01背包的方法求出最大价值。

#include "bits/stdc++.h"
using namespace std;
int bagv = 20;
void completepack(int f[], int w, int v)
{
    for (int j = w; j <= bagv; j++)//完全背包
    {
        f[j] = max(f[j], f[j - w] + v);
    }
}
void zeroonepack(int f[], int w, int v)//01背包
{
    for (int j = bagv; j >= w; j--)
    {
        f[j] = max(f[j], f[j - w] + v);
    }
}
int multiplepack(int f[],int q/*重量*/,int w/*价值*/, int v /*最多取的个数*/)
{
    if (q * v >= bagv)//若大于bagv就按照完全背包
    {
        completepack(f, q, w);
        return 0;
    }
    int k = 1;
    //将数量按二进制分解
    while (k < v)
    {
        zeroonepack(f, k * q, k * w);
        v -= k;
        k *= 2;
    }
    zeroonepack(f, v * q, v * w);
}

int main()
{
    int f[50] = {0};
    int w[6] = {0, 5, 3, 2, 4, 1};//weight
    int v[6] = {0, 6, 4, 3, 5, 2};//value
    int n[6] = {0, 2, 5, 3, 15, 8};//number
    for (int j = 1; j <= 5; j++)
    {
        multiplepack(f, w[j], v[j], n[j]);
    }
    cout << f[bagv];
}

这样不论是01背包,完全背包还是多重背包都有相应的解法,做混合背包问题(某些物品只有一件,某些物品有特定件)只需if-else判断并用相应式子即可。

二维背包

在前三个背包问题基础上有两种不同成本,比如买某件物品需要花x人民币加y美刀。在一维成本基础上再加一维,使用二维数组来存最优数据即可。

F [ i, v. u ] = max(F [ i - 1, v, u ], F[i - 1, v - Ci, u - Di ] + Wi)

分组背包

题目

有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 Ci,价值是 Wi。这些物品被划分为 K 组,每组中的物品互冲突,最多选一件。求解将哪些物品装入背包,可使这些物品的费用总和不超过背包容量,且价值总和最大。

这种问题跟01很相像,其实还是两种情况,对于每一组物品,拿一个或者都不拿,因此只要确保不会多拿即可,其他与01一致。

#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
int main()
{
    int w[5][2] = {{0,0}, {2,3}, {3,3}, {4,4}, {5,1}}; //weight1 & weith2
    int v[5][2] = {{0,0}, {3,4}, {4,5}, {5,4}, {6,2}}; // value1 & value2

    int bagv = 10;//bag's space
    int dp[20] = {0};
    for(int k=1;k<=4;k++)
    {
        for(int j=bagv;j>=0;j--)
        {
            for(int i=0;i<2;i++)//对每一组遍历放在背包空间for循环中,这样能确保不会出现两个都拿的情况
            {
                if(j<w[k][i])
                    continue;
                dp[j]=max(dp[j],dp[j-w[k][i]]+v[k][i]);
            }
        }
    }
    cout << dp[bagv];
}
posted @ 2021-05-23 15:31  Paramecium  阅读(98)  评论(0)    收藏  举报