所以说这就是一篇写炸的废文!!!!

所以说背包直接看dd大神的就好了,算了瞎写写吧。

0/1背包

有n件物品和一个容量为C的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品放入背包可使背包的价值总和最大。

0/1背包是最基础的背包问题,问题的特点是:每种物品只有一件,那么对于第i种物品一共只存在两个状态:取或不取。

定义状态:用f[i][c] 表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。

那么很容易可以得到状态转移方程:

f[i][j]=max{ f[i-1][c] , f[i-1][c-w[i]]+v[i] }

这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下:“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。

如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v]; 如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。(以上两段话来自dd神犇博客)

据dd神犇所讲,对于上述方程,时间复杂度和空间复杂度为O(N*V),时间复杂度已经无法优化,而空间复杂度可以优化;具体操作详见dd大神博客。

①    百度—搜索背包九讲—找一个写的最强大的CSDN博客

②    传送门:http://blog.csdn.net/ling_du/article/details/41594767(搬运)

回到正题,虽然我是讲不清楚(早晚弄懂回来讲一遍)代码还是要放的

for(int i=1;i<=n;i++){

for(int c=C;c>=w[i];c--)

f[c]=max(f[c],f[c-w[i]]+v[i]);

}

需要注意的是,第二层循环的顺序十分重要,如果写成顺推,就会出现已经更新过的状态被重复更新,实际上这可以用来解决完全背包,那么如何才能让每个状态只被更新一次?逆推。我们想一下,我们所写的方程,利用的是c-w[i],如果正着推,不可避免的会出现c-w[i]=之前被更新过的某个状态,但倒着显然就不会,因为c-w[i]是越变越小的,这样就达到了每个物品只取一次的目的。当然…这是我yy+显然的证明法,具体解释还是去dd神犇博客慢慢看吧。

完全背包

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

对于完全背包,它和0/1背包的区别就在于0/1背包的每件物品只能用一次,而完全背包每件物品可以使用无限次。基本解法就是将它转变成0/1背包来解,相较于0/1背包,似乎要多枚举一层件数?

状态转移方程:

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}

这跟01背包问题一样有O(N*V)个状态需要求解,但求解每个状态的时间已经不是常数了,求解状态f[i][v]的时间是O(v/c[i]),总的复杂度是超过O(VN)的。

将01背包问题的基本思路加以改进,得到了这样一个清晰的方法。这说明01背包问题的方程的确是很重要,可以推及其它类型的背包问题。但我们还是试图改进这个复杂度。(来自dd神犇)。

还记得0/1背包中被我yy+显然得出的结论吧…..

没错!0/1背包简化成一维后的第二重循环我们只要把逆推改为正推就行!既然完全背包需要重复计算,那我们就用曾经被我们否决掉的能用来重复计算的方案就好。(更严谨的解释…你们知道我要让你们看什么)。

代码:

for(int i=1;i<=n;i++){

for(int c=w[i];c<=C;c++)

if[c]=max( f[c] , f[c-w[i]] + v[i]);

}

/*来自dd神犇博客:完全背包问题有一个很简单有效的优化,是这样的:若两件物品i、j满足c[i]<=c[j]且w[i]>=w[j],则将物品j去掉,不用考虑。这个优化的正确性显然:任何情况下都可将价值小费用高得j换成物美价廉的i,得到至少不会更差的方案。对于随机生成的数据,这个方法往往会大大减少物品的件数,从而加快速度。然而这个并不能改善最坏情况的复杂度,因为有可能特别设计的数据可以一件物品也去不掉。

这个优化可以简单的O(N^2)地实现,一般都可以承受。另外,针对背包问题而言,比较不错的一种方法是:首先将费用大于V的物品去掉,然后使用类似计数排序的做法,计算出费用相同的物品中价值最高的是哪个,可以O(V+N)地完成这个优化。*/

多重背包

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

和完全背包类似的问题,除了每件物品有固定件数,循环改一下即可,方程:

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}

没错就只是加了一个数量限制而已。

然后我们想着试图优化这个方程。

转化为01背包问题

另一种好想好写的基本方法是转化为01背包求解:把第i种物品换成n[i]件01背包中的物品,则得到了物品数为Σn[i]的01背包问题,直接求解,复杂度仍然是O(V*Σn[i])。

但是我们期望将它转化为01背包问题之后能够像完全背包一样降低复杂度。仍然考虑二进制的思想,我们考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略——取0..n[i]件——均能等价于取若干件代换以后的物品。另外,取超过n[i]件的策略必不能出现。

方法是:将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。

分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示,这个证明可以分0..2^k-1和2^k..n[i]两段来分别讨论得出,例如:

13=2^0+2^1+2^2+6

对于件数足够多的,我们可以将它视为完全背包来写。代码:

for(int i=1;i<n;i++){

if(w[i]*a[i]>C){

for(int c=0;c<=C;c++)

if(c>=w[i])

f[c]=max(f[c],f[c-w[i]]+v[i]);

}

else{

int k=1,amount=a[i];

while(k<amount){//想一想刚才说的二进制法

for(int c=C;c>=k*w[i];c--)

f[c]=max(f[c],f[c-k*w[i]]+k*v[i]);

amount-=k;

k+=k;

}

for(int c=C;c>=amount*w[i];c--)//把剩下的当成一个物品

f[c]=max(f[c],f[c-amount*w[i]]+amount*v[i]);

}

}

混合背包

简而言之,将以上三种背包混在一起,判断一下是哪个背包即可

for(int i=1;i<=n;i++){
  if(num[i]==1){//0/1
    for(int c=v1;c>=w[i];c--)
      f[c]=max(f[c],f[c-w[i]]+v[i]);
  }
  else if(num[i]*w[i]>v1){//完全
    for(int c=w[i];c<=v1;c++)
      f[c]=max(f[c],f[c-w[i]]+v[i]);
  }
  else {//多重
    int k=1,amount=num[i];
    while(k<amount){
      for(int c=v1;c>=k*w[i];c--)
        f[c]=max(f[c],f[c-k*w[i]]+k*v[i]);
      amount-=k;
      k<<=1;
    }
    for(int c=v1;c>=amount*w[i];c--)
    f[c]=max(f[c],f[c-amount*w[i]]+amount*v[i]);
  }
}

 

蒟蒻的9个背包的浩大工程(更新中)(无限延期)(太长了不舍删虽然写的lj的一匹)的更多相关文章

  1. [New!!!]欢迎大佬光临本蒟蒻的博客(2019.11.27更新)

    更新于2019.12.22 本蒟蒻在博客园安家啦!!! 本蒟蒻的博客园主页 为更好管理博客,本蒟蒻从今天开始,正式转入博客园. 因为一些原因,我的CSDN博客将彻底不会使用!!!(带来不便,敬请谅解) ...

  2. 【杂文】NOIP2018 蒟蒻自闭记

    [杂文]NOIP2018 蒟蒻自闭记 都 \(9102\) 年了,谁还记得 \(2018\) 年的事啊 \(QAQ\) . 还有两个月就要去参加首届 \(CSP\) 了. 想着如果再不记下去年那些事儿 ...

  3. NOIp蒟蒻的爆零记——HA-0132

    考前: 从十一月开始的听课集训,连考六场:考前的最后两天写(da)着(zhe)各种各样的奇(C)葩(S)模板:一周的疯狂,已经过去: 考前的一晚:第二批高二的六个人聚在一起(还有滑稽大师),愉快的玩( ...

  4. 【BZOJ-4636】蒟蒻的数列 动态开点线段树 ||(离散化) + 标记永久化

    4636: 蒟蒻的数列 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 247  Solved: 113[Submit][Status][Discuss ...

  5. 【蒟蒻の进阶PLAN】 置顶+持续连载

    看到周围神犇们纷纷列计划,本蒟蒻也决定跟随他们的步伐,计划大约是周计划吧,具体怎么安排我也不确定.. 2015.12.30 刚刚学习完最基础的网络流,需要进行这方面的练习,从简到难,有空余的话尝试学习 ...

  6. [BZOJ4636]蒟蒻的数列

    [BZOJ4636]蒟蒻的数列 试题描述 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将数列[a,b)这个区间中所有比k ...

  7. 蒟蒻修养之cf橙名计划

    因为太弱,蒟蒻我从来没有上过div1(这就是今年的最后愿望啊啊啊啊啊)已达成................打cf几乎每次都是fst...........所以我的cf成绩图出现了惊人了正弦函数图像.. ...

  8. 【BZOJ】4636: 蒟蒻的数列

    4636: 蒟蒻的数列 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 145  Solved: 71[Submit][Status][Discuss] ...

  9. noip2013Day2T3-华容道【一个蒟蒻的详细题解】

    描述 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间. 小 B 玩的华容道与经典的 ...

随机推荐

  1. arc068 E: Snuke Line

    首先要知道 (m/1 + m/2 + ... + m/m) 约为 mlogm 还有一个比较明显的结论,如果一个纪念品区间长度大于d,那么如果列车的停车间隔小于等于d,则这个纪念品一定能被买到 然后把区 ...

  2. [USACO07DEC]美食的食草动物Gourmet Grazers

    ---题面--- 题解: 首先观察题面,直觉上对于一头奶牛,肯定要给它配pi和qi符合条件的草中两者尽量低的草,以节省下好草给高要求的牛. 实际上这是对的,但观察到两者尽量低这个条件并不明确,无法用于 ...

  3. [COGS 622] [NOIP2011] 玛雅游戏 模拟

    整个模拟的关键除了打出来就是一个剪枝:对于两个左右相邻的块你不用再走←,因为走→是等效的 #include<cstdio> #include<cstring> #include ...

  4. js金额转大写数字

    //金额转大写数字 const intToChinese = money => { //汉字的数字 let cnNums = new Array('零', '壹', '贰', '叁', '肆', ...

  5. POJ2502:Subway(最短路)

    Subway Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 14634   Accepted: 4718 题目链接:http ...

  6. POJ3349 Snowflake Snow Snowflakes (hash

    Snowflake Snow Snowflakes Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 48624   Accep ...

  7. JAVA中string.replace()和string.replaceAll()的区别及用法

    乍一看,字面上理解好像replace只替换第一个出现的字符(受javascript的影响),replaceall替换所有的字符,其实大不然,只是替换的用途不一样.    public String r ...

  8. lwIP内存管理机制

    lwip的内存管理机制,我们以enet_lwip这个例程为例. 在使用lwip的时候,我们可以使用两种形式的内存,一种是heap(mem.c文件-mem_malloc()),一种是pool(memp. ...

  9. idea使用(一)

    基本上正式开发的常用工具基本都集成了,而且基本都在你非常容易触到的位置.说说我比较常用的: 1.ant 你懂的 2.maven你也懂的 3.SVN相比之下,IDEA的SVN的提交提供了更多的选项和功能 ...

  10. 模拟实现jdk动态代理

    实现步骤 1.生成代理类的源代码 2.将源代码保存到磁盘 3.使用JavaCompiler编译源代码生成.class字节码文件 4.使用JavaCompiler编译源代码生成.class字节码文件 5 ...