膜背包九讲有感ORZ

  本咸鱼终于停课了,在Mr.pan的支持下膜了一波背包九讲,为了方便自己以后复习,将先在的一些感悟记录下来。

1.01背包

  这种背包是最基础的背包,题目大概是这样:

    有 N 件物品和一个容量为 V 的背包。放入第 i 件物品耗费的费用是 Ci,得到的 价值是 Wi。求解将哪些物品装入背包可使价值总和最大。

  这个最初始的状态就是f[i][j],f[i][j]表示的是装到第i件物品(无论是否装入),使用了容量为j的背包(无论有没有装满)的时候,所能获得的最大价值。这样的话状态转移方程式就不难想出了f[i][j]=max(f[i-1][j],f[i-1][j-c[i]]+w[i])。外层再套一个双重循环,枚举i,j就可以了。代码如下

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

  然后,我们在求f[i][j]时,只使用了f[i-1][0~(j-c[i])]中的值,然而当我们需要求f[i+1][...]时又只需要使用f[i][...]中的值,所以我们就可以只把i开到2,循环使用数组,代码也就变成了这样:

  

    int i,j,k=0;
    for(i=1;i<=n;i++)
    {
        k=k^1;
        for(j=1;j<=c[i];j++)
        {
            f[k][j]=max(f[k^1][j],f[k^1][j-c[i]]+w[i]);
        }
    }

虽然没有节省时间,但节省了非常多的空间。

  背包九讲中还提供了一种优化空间的方法,也就是f数组只开一维,然后枚举j的时候从m到c[i]枚举,这样就防止了多次选取这件物品,具体代码如下:

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

  然而背包九讲上还提供了一种优化常数的方法,只不过本咸鱼没有看懂,等看懂后补上吧。

2.完全背包

  这种背包跟0背包唯一的区别就在于每件物品可以无限使用。这样的话状态可以沿用01背包的状态,只不过状态转移方程需要改一下f[i][j]=max(f[i-1][j],f[i-1][j-k*c[i]]+k*w[i])。代码的话只需要在j循环里面枚举一下k就行了。代码如下:

    for(i=1;i<=n;i++)
    {
        for(j=c[i];j<=m;j++)
        {
            for(k=1;k<=j/c[i];k++)
            {
                f[i][j]=max(f[i-1][j],f[i-1][j-k*c[i]]+k*w[i]);
            }
        }
    }

  还记得刚刚讨论01背包时说的那种一维数组的做法吗,当时说的是j只能从m到c[i]进行枚举,不然可能就会将这件物品选择多次。但是这道题每件物品不是可以无限次选取吗,正好。

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

  当然,背包九讲上还是提供了一种玄学的方法,就是将i这件物品拆分成多件物品,每件物品的费用为2^k*c[i],价值为2^k*w[i],这样我们就可以组合成任意个背包的总和。(当学了倍增后再看这个,真的感觉很玄学)由于本咸鱼实在不想写这个版本的代码,这种玄学的方法就留给读者去实现啦。

3.多重背包

  这个相比较完全背包就多了一个每件物品数量的限制,最简单的方法就是在最暴力的完全背包的代码的基础上改成k<=当前物品的件数。代码就不贴了,反正就改了一个地方,本咸鱼懒……

  优秀一点的做法便是利用类似倍增的思想来枚举物品的数量(就是上面说的2^k那种方法),代码懒得写,反正应该不会考吧……(一口毒奶)。

  然后背包九讲上的O(VN)的算法感觉没怎么看懂,看懂后再更新一下吧。

4.混合背包

  混合了上面三种物品的背包,这个我一般就写几个函数,到时候直接按物品调用函数就ok了,状态的话就统一使用最初的那个f[i][j]表示第i件物品,容量为j的背包……的那个状态,虽然时间复杂度可能比较高,但出错的几率可以降低很多,就不用那么痛苦地调代码了。

5.二维费用的背包问题

  这个就是每件物品所需要的费用多了一种,其他的条件没有变。

  这样的话就把状态加一维,枚举状态的时候多枚举一维就Ok了。状态转移方程式就是这样f[i][j][k]=max(f[i-1][j][k],f[i-1][j-c[i]][k-d[i]]+w[i])。

  让我比较在意的是背包九讲中所提到的这种“隐含的第二维费用”:

    “有时,“二维费用”的条件是以这样一种隐含的方式给出的:最多只能取 U 件物品。 这事实上相当于每件物品多了一种“件数”的费用,每个物品的件数费用均为 1,可以 付出的最大件数费用为 U。换句话说,设 F[v,u] 表示付出费用 v、最多选 u 件时可得 到的最大价值,则根据物品的类型(01、完全、多重)用不同的方法循环更新,最后在 f[0...V,0...U] 范围内寻找答案。(我觉得他讲得够明白了,就不做过多解释了)”

  对,没有错,就是类似这种看起来根本不像是二维费用的背包,本咸鱼感觉根本想不到啊,算了,写完这篇博客多做题吧。

  多吐槽一句,在这种背包的末尾背包九讲的作者写了这样一句话:“当发现由熟悉的动态规划题目变形得来的题目时,在原来的状态中加一维以满足新的限制是一种比较通用的方法。希望你能从本讲中初步体会到这种方法。”我体会不到啊……,怎么办,我退群哦不退役算了……

6.分组背包

  这种背包我对他始终没有什么感觉,每次碰见还是不会做,ORZ:

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

  好吧,因为每组的物品最多选一个,所以我们就可以把它当作01背包来看,f[k][j]表示前k组物品中,装一个背包容量为j的背包的最大价值。f[k][j]=max(f[k-1][j],f[k-1][j-c[i]]+w[i])i ∈group k,久违地来贴一下代码吧。

    vector<int>group_k[MAX_K];//记录group k中有哪些物品
    for(k=1;k<=K;k++)
    {
        for(j=0;j<=m;j++)
        {
            int n=group_k[k].size();
            for(i=0;i<n;i++)
            {
                if(c[i]<=j) f[k][j]=max(f[k-1][j],f[k-1][j-c[i]]+w[i]);
            }
        }
    }

  如果f数组要降维的话就按照01背包降维那样降维,并且j从m到0循环就可以了。

7.有依赖的背包问题

  既然背包九讲中提到了金明的预算方案那我就拿金明的预算方案来说这个吧。

  金明的预算方案可以说是有依赖的背包问题中的裸题了题目在此我也不赘述了,就说说应该怎么做吧。因为附件必须依赖主件,所以当我们要选择附件的时候必须连同主件一起选择,所以我们就可以开一个辅助数组来确定这件主件,以及其所可以携带的附件是否携带,或者是携带哪几个,意思就是在必须选择本主件的情况下,对其附件进行01背包,然后将这个辅助数组的值与主数组比较。可能说得不是很明白,直接上代码应该更直观吧。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int v[100],p[100],q[100],n,m,s[200000],t[200000];
void Init()
{
    memset(s,0,sizeof(s));
    memset(t,0,sizeof(t));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&v[i],&p[i],&q[i]),p[i]*=v[i];

}
void work()
{
    int i,j,k;
    for(i=1;i<=m;i++)
    {
        if(q[i]==0)
        {
            for(j=1;j<=v[i];j++) t[j]=0;
            for(j=v[i];j<=n;j++) t[j]=s[j-v[i]]+p[i];
            for(j=1;j<=m;j++)
            {
                if(q[j]==i)
                {
                    for(k=n;k>=v[i]+v[j];k--)
                    {
                        t[k]=max(t[k],t[k-v[j]]+p[j]);
                    }
                }
            }
            for(j=v[i];j<=n;j++)
            {
                s[j]=max(s[j],t[j]);
            }
        }
    }
    printf("%d",s[n]);
}
int main()
{
    Init();
    work();
    return 0;
}

  我最初想的状态本来是f[i][j]表示选择到第i个主件的时候,背包容量为j时所获得的最大价值,但后来发现比较难写,并看了题解后才确定了最后的状态。以后这种题还是需要多练(前提是在我不会今年noip后退役)

8.泛化物品

  泛化物品简单来说就是物品的价值会随你分配给它的费用而改变,如果是01背包,假设这件物品的价值-费用函数关系为h(x),f[j]表示费用为j的背包能获得的最大价值。那么状态转移方程式也就不难得出f[j]=max(f[j],f[j-x]+h[x]).其他形式的背包也应该差不多吧……

9.背包问题问法的变化

  由于本咸鱼学艺不精,加上背包问题的千变万化,这一点我就不写了,怕误导诸位,加上误导我自己。

10、好像有一种背包没有提到

  在本咸鱼的的记忆中好像有一种背包要求必须装满。这种背包做法和01背包相似,但是数组赋初值的时候一定要注意,初值用memset应该赋为0x80!!!!!!

时间: 2024-10-12 15:38:32

膜背包九讲有感ORZ的相关文章

背包九讲之多重背包

背包九讲原文: 题目 有N种物品和一个容量为V的背包.第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本算法 这题目和完全背包问题很类似.基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件.令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程: f[i][v]=max{f[i-1][v-k*c[i]]

完全背包(背包九讲)

题目: 有N种物品和一个容量为V的背包,每种物品都有无限件可用.第i种物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 思路: 这个问题非常类似于01背包问题,所 不同的是每种物品有无限件.也就是从每种物品的角度考虑,与它相关的策略已并非取或不取两种,而是有取0件.取1件.取2件--等很多种.如果仍然按照解 01背包时的思路,令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值.仍然可以按照每种物品不同的策略写出状态转

01背包问题的学习(来源:背包九讲)

问题: 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使价值总和最大. 思路: 这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放.用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值.则其状态转移方程便是:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}.这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的.所以有必要将它详细解释一下:"

有依赖的背包问题(背包九讲)

问题: 这种背包问题的物品间存在某种"依赖"的关系.也就是说,i依赖于j,表示若选物品i,则必须选物品j.为了简化起见,我们先设没有某个物品既依赖于别的物品,又被别的物品所依赖:另外,没有某件物品同时依赖多件物品. 算法: 这个问题由NOIP2006金明的预算方案一题扩展而来.遵从该题的提法,将不依赖于别的物品的物品称为"主件",依赖于某主件的物品称为"附件".由这个问题的简化条件可知所有的物品由若干主件和依赖于每个主件的一个附件集合组成.按照背

二维费用背包问题(背包九讲)

------------------------------------------ 前言: 对于一些背包问题,重点还是在于如何找出"背包容量"和"各种代价",以及价值,如此问题便迎刃而解了.下午 打篮球居然下冰雹了,悲催了.... ------------------------------------------ 问题: 二维费用的背包问题是指:对于每件物品,具有两种不同的费用:选择这件物品必须同时付出这两种代价:对于每种代价都有 一个可付出的最大值(背包容量)

转载:《背包九讲》

<背包九讲> P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放. 用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值.则其状态转移方程便是:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}. 这个方程非常重要,基

转载DD大神背包九讲

dd大牛的<背包九讲> P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放. 用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值.则其状态转移方程便是:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}. 这个方程非

多重背包问题(来源:背包九讲)

问题: 有N种物品和一个容量为V的背包.第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本算法: 这题目和全然背包问题非常类似.主要的方程仅仅需将全然背包问题的方程稍微一改就可以,由于对于第i种物品有n[i]+1种策略:取0件,取1件--取n[i]件.令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:f[i][v]=max{f[i-1][v-k*c[i]]+k*w

分组的背包问题(背包九讲)

在使用Ubuntu作为开发环境时经常需要在全局安装一些依赖框架等,这个时候就常常需要用到root权限,但是在Ubuntu下第一次使用su命令时会提示认证失败:查找资料后发现Ubuntu下root权限默认是锁定的,可能是处于安全考虑,但是作为开发人员肯定是需要root权限的. 在命令行中可以输入下面命令设置root密码,这样就能随时使用root权限了: [email protected]:~$ su 密码: su:认证失败 [email protected]:~$ sudo passwd [sud