模板背包

Posted daz-os0619

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板背包相关的知识,希望对你有一定的参考价值。

(死亡)

本文部分参照背包九讲(链接点这里)

先看三道题:

01背包完全背包混合背包

请记住这个题面:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。

有相似题面的一系列一种组合优化的NP完全问题(附百度百科)就是背包问题

背包的种类很多,大概(我会的)有01背包完全背包多重背包分组背包

从最基础的完全背包讲起。

完全背包

题面:

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

思路:

有F[u][v]用来存装了u种物品的背包里的最大价值。

对加入x个物品k的决策,有f[k][v]=f[k-1][v-c[k]*x]+w[k]*x

状态转移方程:f[k][v]=max{ f[k-1][v-c[k]*x]+w[k]*x (0<=k<=v/c[k]) }

时间复杂度O(VΣ(V/c))

空间复杂度O(NV)

 还有改进空间。。

先简单优化下:

技术图片

拿两个的状态可以从拿一个的转移而来,后面以此类推

(仔细观察还可以再优化的亚子??)

优化代码大概长这样:

for(int i=1;i<=n;i++)
    {
        for(int j=w[i];j<=m;j++)
        f[j]=max(f[j-w[i]]+v[i],f[j]);
    }

完全背包就差不多了。。

画图来说就是这样的:
(二维)

技术图片

 

 
再转成一维看一下:
技术图片
 
这样就解释了为什么第二重循环要从小到大。 

01背包

题面:

有N种物品和一个容量为V的背包,每种物品都只有1件可用。第i种物品的费用是ci,价值是wi。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

思路:

跟完全背包差不多,完全背包看懂了这个也就自然OK

连代码都差不多的亚子,但是还是有一点不一样

先看代码找不同:

 for(int i=1;i<=n;i++)
    {
        for(int j=m;j>=w[i];j--)
        f[j]=max(f[j-w[i]]+v[i],f[j]);
    }

为什么内循环是从大到小?

画图解释一下:

技术图片

由此图可知:01背包依赖于已知前置位置

再改成一维的看看:

技术图片

所以循环是从大到小

多重背包

题面:

有N种物品和一个容量为V的背包,每种物品都有n件可用。第i种物品的费用是ci,价值是wi。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

思路:

先介绍“二进制分组”

将一个自然数n分成m个数相加的形式,且1-n间的每一个数都可以被分成的m个数表示

例:

n=m1+m2+m3;

1-n中的所有数都可以用m1,m2,m3的和来表示

怎么实现就要用到万能的二进制了

将一个自然数n分成

n=a1*21+a2*22+a3*23+……+an*2n

这是会发现,这样并不能把所有自然数分完,特别是奇数,总会再剩一点。

所以设m=a1*21+a2*22+a3*23+……+an*2n

那么n就可以被表示成

n=a1*21+a2*22+a3*23+……+an*2n+(n-m)

而多重背包就是把n个物品二进制分组再重新处理它们的体积和价值,再用01背包即可。

代码:

for(int i=1;i<=n;i++)
    {
        int t=1;
        if(p[i]>1)
        {
            while(p[i]>t)
            {
                k++;
                w[n+k]=w[i]*t;
                v[n+k]=v[i]*t;
                p[n+k]=1;
                p[i]-=t;
                t*=2;
            }
            w[i]*=p[i];
            v[i]*=p[i];
            p[i]=1;
        }
    }
     
    for(int i=1;i<=n+k;i++)
    {
        if(p[i]==1)
        {
            for(int j=m;j>=w[i];j--)
            {
            f[j]=max(f[j],f[j-w[i]]+v[i]);
            }
        }

看一道例题:(点击收获RP++)

就很简单。。01背包就可以解决

AC代码:

#include<iostream>
#include<cstdio>
using namespace std;
int v,c,n;
int w[10001],f[10001],p[10001];//
bool vis[10001];
int main()
{
    scanf("%d%d%d",&v,&n,&c);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&w[i],&p[i]);
    }
//    f[0]=-0x7ffffff;
    for(int i=1;i<=n;i++)
    {
        for(int j=c;j>=p[i];j--)
        {
        
            f[j]=max(f[j],f[j-p[i]]+w[i]); 
        }
    }
    for(int i=1;i<=c;i++)
    {
        if(f[i]>=v)
        {
        printf("%d",c-i);
        return 0;
        }
        
    }
    printf("Impossible");
    return 0;
}

(RP++!)

以上是关于模板背包的主要内容,如果未能解决你的问题,请参考以下文章

01背包模板全然背包 and 多重背包(模板)

用回溯法求01背包问题,怎样使用C++模板啊,迫切求指点!

VSCode自定义代码片段——.vue文件的模板

解题报告:hdu2191汶川地震 - 多重背包模板

VSCode自定义代码片段1——vue主模板

VSCode自定义代码片段2——.vue文件的模板