zoukankan      html  css  js  c++  java
  • 背包问题

    0/1 背包


    题目

    N 件物品和一个容量为 V 的背包. i 种物品的的费用是 c[i], 价值是 w[i], 每种物品只有一件, 求解将哪些物品装入背包可使价值总和最大

    思路

    f[i][v] 表示前 i 件物品放到一个容量为 v 的背包可以获得的最大价值

    f[i][v] = max( f[i-1][v], f[i][v-c[i]]+w[i] ) 表示第i件物品不放或者放背包两种情况

    优化

    f[i][j] 是一个 NV列的矩阵, 动态规划的过程就是一行一行的往矩阵里填值

    i行仅调用第i-1行的值. i行的第v列调用第i-1行的第v1,v2, 并且v1,v2<=v

    基于如上观察, 可对空间优化

    for v= V…0

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

    逆序遍历, new 指逻辑上的第i行, old指逻辑上的第i-1

    完全背包问题


    题目

    0/1背包的区别在于每种物品有无穷多件

    思路

    f[i][v]表示前i件物品放到容量为v的背包可获得的最大价值

    f[i][v] = max(f[i-1][v-0*c[i]]+0*w[i], f[i-1][v-1*c[i]]+1*w[i]…f[i-1][v-j*c[i]]+j*w[i]) 枚举第i件物品放入背包的数目, 此公式可递归表示为

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

    优化

    f[i][j]是一个NV列的矩阵, 动态规划的过程就是一行一行的往矩阵里填值

    i行不仅调用第i-1行的值, 还调用第i行的值

    for v = 0…V

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

    顺序遍历, 保证f[v]在逻辑上是第i-1行的值, f[v'](v'<v)逻辑上是第i行的值

    完全背包的一个变形


    题目

    HDOJ 4276

    N个节点, i个节点投资 j 分组可获得 value[i][j] 的收益, 求解最大收益

    思路

    划分的粒度比上面的完全背包更细了些

    f[i][v] = max(f[i-1][v-0], f[i-1][v-1]+value[i][1]…f[i-1][0]+value[i][v]) 表示在第i个节点上投资的时间从0->max

    优化

    for t = 0…T

        for k = 0…t

            f[t] = max(f[t], f[t-k]+value[i][k])

    这里多了第二层关于k的循环, 因为不像朴素完全背包问题那样, 物品的价值与个数成正比, 而是, 投资的时间与获得的收益关系由value给出

    最外层循环应该是 for t = T...0. 

    投资的时间与获得收益关系由 value 给出, 而不是与个数成正比让这道题不能使用递归的方式求解, 必须使用三层循环

    使用递归的方式求解只有两层循环, 且t是从小到大. 不使用递归方式求解时有三层循环, t是从大到小. 被划线的代码写成 4 不像了

    分组背包问题


    题目

    N件物品和容量为V的背包,i件物品的费用是c[i], 价值是w[i].这些物品被分为若干组, 每组中的物品选择时相互冲突, 最多选择一件. 求解哪些物品放入背包可使获得的总价值最大

    思路

    对组遍历, 枚举组中的物品选取1件或者一件都不选取

    f[k][v] = max(f[k-1][v], f[k-1][v-c[i]+w[i]]) i为第k组的某一件物品

    算法

    for 所有的组

        for v= V…0

            for 所有的i属于组k

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

    三维背包


    题目

    九度棋盘寻宝和棋盘寻宝扩展

    8*8 的棋盘, 64个格子上放在礼物, 每个礼物有一定的价值. 人的初始位置在左上角, 求解此人走到右下角时能拿到的不超过limit的最大价值, 人每次可以选择向下走或向右走

    思路

    i行第j列的礼物价值为 w[i][j], 代价为c[i][j], w[i][j] ==c[i][j]

    f[i][j][v]表示走到第i行第j列时不超过limit的最大价值

    那么f[i][j][v] = max(f[i-1][j][v-c[i][j]], f[i][j][v-c[i][j]])+w[i][j])

    代码

    for(int i = cl-1; i >= 0; i --) {

            for(int j = cl-1; j >= 0; j--) {

                for(int k = 0; k <= limit; k ++) {

                    if(i+1 == cl && j+1 ==cl) break;

                    right = getNeighbor(i, j+1, k-chessboard[i][j]);

                    down = getNeighbor(i+1, j, k-chessboard[i][j]);

                    result[i][j][k] = max(right, down) + chessboard[i][j];

                }

            }

        }

    BTW


    1. 空间的优化看着舒服, 想的时候要多绕一道弯, 我做题时, 实际上都不进行空间压缩. 曾在course 上做25个点的TSP, 那就是那道题是非优化空间不可的
    2. 上面写"i行调用第i-1"行的值. 这里调用往往有主动request的感觉, 而在实现floydwarshall 算法时感受到了push的效果, 即第i-1行去主动更新第i
    3. 研一上算法课. 近似算法也讲到了背包问题的求解, 当时还很疑惑, 为什么一个被完美解决的题目还要研究近似算法. 问了下老师发才了解到上面的背包问题有一个共性, 容量不会太大, 且容量往往为整数, 这可以理解成是比较理想的情况, 现实生活中的背包问题, 往往没有这么强的假设
    4. DP 遍历时, 最外层的变量应该是独立的, 正逆序一定要搞清楚. 完全背包代码中的递归求解与正常的三重循环通过HDOJ4276 有了新的认识
  • 相关阅读:
    《例说51单片机(C语言版)(第3版)》——1-3 认识MCS-51的存储器结构
    MySQL5.7中InnoDB不可不知的新特性
    python 抓取日志
    Packet for query is too large (1706 > 1024). You can change this value on the server by setting the
    面对1.3 亿用户数据泄露,企业如何围绕核心数据构建安全管理体系?
    python 正则替换
    python 正则
    没有宫廷内斗,数据库界的延禧攻略
    抽象基类
    ansible -m shell -a 注意单引号和双引号
  • 原文地址:https://www.cnblogs.com/xinsheng/p/3426110.html
Copyright © 2011-2022 走看看