• 【模板】背包


    (死亡)

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

    先看三道题:

    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++!)

  • 相关阅读:
    基于Spring+SpringMVC实现AOP日志记录功能service注入异常为null的解决办法
    关于SpringBoot项目打包没有把依赖的jar包一起打包的解决办法
    JavaFx项目打包成exe,并集成Jre,使Java项目在任意机器运行
    常用正则表达式
    SqlServer 2005及其以上版本能用的查询数据的行号
    js 中的倒计时功能
    数据库删除重复列
    【转】svn文件清除批处理工具
    JS获取当前页面名称
    sql 去除重复记录
  • 原文地址:https://www.cnblogs.com/Daz-Os0619/p/11469765.html
Copyright © 2020-2023  润新知