• [经典] 背包问题(一)


    【1】01背包

    N个物品,占容c[i],价值w[i],放入1个容量为V的背包,使得总价值最大

    分析:每种物品仅有一件,可以选择放或不放

    转移方程:opt[i][v] = max{opt[i - 1][v], opt[i - 1][v - c[i]] + w[i]}

    复杂度:时间空间均为O(NV),空间复杂度可压缩,用opt[v]表示,但需要注意的是v必须从V到0遍历,否则逻辑错误;

    初始化技巧:如果要求刚好装满,则设为负无穷;如果只要求最大,则设为0即可。

    事实上,由于对某件物品的01处理问题会在不同情景下都被调用,所以可以写成一个调用函数ZeroOnePack(cost, weigth),其中v的下限可被优化

    procedure ZeroOnePack(cost,weight)     
        for v=V..cost 
            f[v]=max{f[v],f[v-cost]+weight} 

    所以本题的伪代码可以写成

    for i=1..N 
        ZeroOnePack(c[i],w[i]);

    另外,其实下限可以进一步被优化,当V很大时有效,此时本题的伪代码可以写成

    for i=1..n 
        bound=max{V-sum{w[i..n]},c[i]}     
        for v=V..bound     
       f[v]=max{f[v],f[v-cost]+weight} 

    【2】完全背包

    特点是每件物品的数目都是没有限制的,可以由01背包延展出解法,即f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]}, 0<=k*c[i]<=v;复杂度很显然上升了O(v/c[i]),所以尝试找到优化的方法。最简单的优化:c[i] < c[j] && w[i] > w[j],则显然应该选择w[i],去除j物品。

    先是最小均分,将每个物品当成有v/c[i]个,则完全背包等效于01背包,复杂度仍比01上升O(v/c[i]);然后是指数拆分,将每个物品拆分成费用为c[i]*2^k、价值为w[i]*2^k的子物品,则复杂度只上升O(log(v/c[i]))。

    经典解法:复杂度与01背包一样,只有O(VN)。动态规划方程为f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]},而空间复杂度为O(V)的算法只需要将v从0到V顺序遍历。

    procedure CompletePack(cost,weight)     
        for v=cost..V 
            f[v]=max{f[v],f[v-c[i]]+w[i]}

    事实上,v循环与i循环的次序可以颠倒

    【3】多重背包

    特点是每件物品的数目有中庸的限制,即第i件物品的数目限制是n[i]。最直接的方法也是拆分01背包,f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]}, 0<=k<=n[i],复杂度为O(V*Σn[i]);然后是指数拆分,也是经典解法,复杂度为O(V*Σlog n[i])。

    procedure MultiplePack(cost,weight,amount)     
        if cost*amount>=V 
            CompletePack(cost,weight)         
            return     
        integer k=1     
        while k<amount 
            ZeroOnePack(k*cost,k*weight)         
            amount=amount-k        
            k=k*2 
            ZeroOnePack(amount*cost,amount*weight)

    还有复杂度更低的方法,复杂度与01背包一样,只有O(VN),方法是单调队列优化,超了NOIP范围。

    【4】混合背包

    将前三种背包过程分类混合使用,伪代码:

    for i=1..N 
        if 第i件物品属于01背包        
            ZeroOnePack(c[i],w[i])     
        else if 第i件物品属于完全
            CompletePack(c[i],w[i])    
        else if 第i件物品属于多重背包         
            MultiplePack(c[i],w[i],n[i])         

    【5】二维费用背包

    多用一个状态记录多出来的费用,设f[i][v][u]表示前i件物品付出两种代价分别为v和u时可获得的最大价值。状态转移方程就是: 

    f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]} 
  • 相关阅读:
    Fiddler显示响应时间
    WPF RichTextBox
    设计模式摘要
    sqlite insert时一直报constraint 约束
    try catch使用规则
    关于WPF的2000件事 06--WPF布局
    SmartAssembly 汉化说明
    GP80系列打印机驱动 V17下载地址
    记录 java 安卓 各类引用包报错处理方法 例如 android.support.v4.app.+ ,io.reactivex.+
    第一讲 新手如何学习HTTP协议之实践项目多开微信机器人
  • 原文地址:https://www.cnblogs.com/littletail/p/5413242.html
Copyright © 2020-2023  润新知