背包DP的标准代码模板

提前声明,关于背包的思路在背包九讲里面已经很清楚了,这里只是把伪代码写成程序而已。

01背包

首先从最简单的01背包开始。

  有N件物品和一个容量为V 的背包。放入第i件物品耗费的空间是Ci,得到的价值是Wi。求解将哪些物品装入背包可使价值总和最大。
  
标准代码以及注释如下

#include
using namespace std;
#define MAXN 10000+5
int f[MAXN],V;
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{ 
    int n;
    cin >> n >> V;//n是物品的个数,V是背包的体积。
    for(int i=1,w,v;i<=n;i++)
    {
        cin >> w >> v;//w是物品的价值,v是物品的体积。
        for(int i=V;i>=v;i--)
            f[i]=max(f[i],f[i-v]+w);
    }
    cout << f[V];
    return 0;
}

我们可以把01背包写成函数的形式,这样在调试的时候会方便很多。
那么函数版本如下

void ZeroOnePack(int w,int v)
{
    for(int i=V;i>=v;i--)
        f[i]=max(f[i],f[i-v]+w);
}

完全背包

01背包之后,让我们来看看完全背包。

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

标准代码及注释如下

#include
using namespace std;
#define MAXN 10000+5
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int f[MAXN],n,V;
    cin >> n >> V;
    for(int i=1,w,v;i<=n;i++)
    {
        cin >> w >> v;
        for(int i=v;i<=V;i++)//其实就是把01背包反过来循环而已。
            f[i]=max(f[i],f[i-v]+w);
    }
    cout << f[V];
    return 0;
}

同上,函数版本如下

void CompletePack(int w,int v)
{
    for(int i=v;i<=V;i++)
        f[i]=max(f[i],f[i-v]+w);
}

多重背包

那么,我们给每个物品加上数量的限制。

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

标准代码以及注释如下

#include
using namespace std;
#define MAXN 10000+5
int main()
{
    int f[MAXN],n,V;
    cin >> n >> V;
    for(int i=1,w,v,num;i<=n;i++)
    {
        cin >> w >> v >> num;//这里多的num是每种物品的数量
        if(num*v>=V)//如果说只拿这一种物品就能把背包放满,我们就把它视为完全背包。
            for(int j=v;j<=V;j++)
                f[j]=max(f[j],f[j-v]+w);
        else//这里是将多重背包转换成01背包来做,可以单步执行几次,或者头脑加纸笔模拟一下,以加深理解。
        {
            int k=1;
            while(kfor(int j=V;j>=k*v;j--)
                    f[j]=max(f[j],f[j-k*v]+k*w);
                num-=k;
                k*=2;
            }
            for(int j=V;j>=num*v;j--)
                f[j]=max(f[j],f[j-num*v]+num*w);
        }
    }
    cout << f[V];
    return 0;
}

函数版本如下

void MultiPack(int w,int v,int num)
{
    if(num*v>=V)
        for(int j=v;j<=V;j++)
            f[j]=max(f[j],f[j-v]+w);
    else 
    {
        int k=1;
        while(kfor(int j=V;j>=k*v;j--)
                f[j]=max(f[j],f[j-k*v]+k*w);
            num-=k;
            k*=2;
        }
        for(int j=V;j>=num*v;j--)
            f[j]=max(f[j],f[j-num*v]+num*w);
    }
}

我们还可以用01背包和完全背包的函数简化一下这个代码

void MultiPack(int w,int v,int num)
{
    if(num*v>=V)
        CompletePack(w,v);
    else 
    {
        int k=1;
        while(k*w,k*v);
            num-=k;
            k*=2;
        }
        ZeroOnePack(num*w,num*v);
    }
}

混合背包

混合背包就是说一道题里面会有01背包,会有完全背包,会有多重背包。对于这种题目,我们只需要把前面三种背包的函数做一个整合,然后判断是哪一种物品,调用相应的函数即可。

泛化物品

  考虑这样一种物品,它并没有固定的费用和价值,而是它的价值随着你分配给它的费用而变化。这就是泛化物品的概念。

泛化物品并没有什么固定的代码,它是随着题目给出的关系式变化而变化的。我们要根据不同题目的要求来进行改动,但大致是这样的思路:

void GeneralizationPack(...)
{
    for(int i=V;i>=1;i--)
        for(int x=1;x<=i;x++)
            f[i]=max(f[i],f[i-x]+...);
}

【P.S.】…在这里代表的是不固定的内容,会根据题意变化的

那么假设我们分配的体积x与价值y的关系是这样y=a*x^2-b*x,则

void GeneralizationPack(int A,int B)
{
    for(int i=V;i>=1;i--)
        for(int x=1;x<=i;x++)
            f[i]=max(f[i],f[i-x]+A*x*x-B*x);
}

你可能感兴趣的:(动态规划)