• 背包dp


    背包dp

    背包dp

    ◦一般是给出一些“物品”,每个物品具有一些价值参数和花费参数,要求

    在满足花费限制下最大化价值或者方案数。

    ◦最简单几种类型以及模型

    ◦ 0/1背包

    ◦完全背包

    ◦多重背包

    0/1背包

    给出n个物品,每个物品有Vi的价值和Wi的费用,我们总共有m块钱,求

    最多能得到多少价值的物品。

    二维写法:dp[i][j]表示前i个物品,花费j元,能得到的最大价值
    memset(dp,-0x3f,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<w[i];j++)dp[i][j]=dp[i-1][j];
        for(int j=w[i];j<=m;j++)dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
    }    
    一维写法:f[j]表示在前i-1轮的选择后,花费j空间的背包,能得到的最大值
    memset(f,-0x3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++)
        for(int j=m;j>=w[i];j--)//一定要注意枚举的顺序!!!
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    复杂度:O(n*m)
    

    方案数:储存两个dp值,一个f表示最大价值,一个dp表示方案数,如果选i和不选i的最大价值相同,那么dp[j]=dp[j]+dp[j-w[i]]

    具体方案:用二维dp可以很容易的用递归求出。

    完全背包

    问题模型:每一个物品可以选无限个

    二维写法:dp[i][j]表示前i个物品,花费j元,能得到的最大价值
    memset(dp,-0x3f,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<w[i];j++)dp[i][j]=dp[i-1][j];
        for(int j=w[i];j<=m;j++)dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]]+v[i]);//与01背包唯一的细小差别i->i-1,dp[i-1][j-w[i]]的值已经被dp[i][j-w[i]]继承过了
    }    
    memset(f,-0x3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++)
        for(int j=w[i];j<=m;j++)//一定要注意枚举的顺序!!!
            f[j]=max(f[j],f[j-w[i]]+v[i]);
    复杂度:O(n*m)
    

    当面对随机数据时,完全背包可以先贪心筛除掉一些不必要的物品(体积相同选价值最大;体积更大,价值更小,直接舍弃),可以极大增加时间效率。

    多重背包

    问题模型:对每一个物品,最多能用t[i]次。

    暴力的方法:直接枚举这个物品用了多少次
    二维写法:
    memset(dp,-0x3f,sizeof(dp));
    dp[0][0];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
        {
            dp[i][j]=dp[i-1][j];
            for(int k=1;k<=t[i]&&k*w[i]<=j;k++)
                dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]]+k*v[i]);
        }
    一维写法:
    memset(f,-0x3f,sizeof(f))l
    f[0]=0;
    for(int i=1;i<=n;i++)
        for(int j=m;j>=0;j--)
            for(int k=1;k<=t[i]&&k*w[i]<=j;k++)
                f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);
    复杂度:O(n*m*t[i])
    
    
    优化一:二进制拆分原理
    将t[i]个物品拆成1+2+4+8…2^k+x, 这样k+1组,x小于2^(k+1) ,然后我们会发现这些组能拼成0…t[i]每一种情况,然后这样我们就成了n*log(t[i])个物品的0/1背包问题。            对于[0,2^(k+1)-1]都能拼成,所以[x,2^(k+1)-1+x]也都能拼成,x<=2^(k+1)-1, 则[0,2^(k+1)-1+x]全能拼成。
    复杂度:O(n*m*log(t[i]))
    优化二:单调队列优化
    理解起来难度较大,我看了好久才看懂(假装看懂)
    我们先观察一下二维的dp方程
    dp[i-1][j-k*w[i]]+k*v[i]
    我们发现对于第二维,我们的j和能转移过来的j-w[i]*k在模w[i]意义下是同余的,也就是说我们可以对于第二维按照模w[i]进行分类,不同类之间不会互相影响
    设f[j]=dp[i-1][j*w[i]+r]。r是我们枚举模w[i]的一个类
    dp[i][j*w[i]+r]=max(f[k]+(j-k)*v[i]);j-k<=t[i]
    我们把式子转化一下
    dp[i][j*w[i]+r]=max(f[k]+k*v[i])+j*v[i];j-k<=t[i]
    实际上就是一个滑动窗口取最值的问题,直接单调队列优化即可
    复杂度:O(n*m)
    

    分组背包

    ◦一共有n组,每组有size[i]个物品,第i组第j个物品的费用为w[i][j],价值

    v[i][j],每个组里的物品是互斥的,意味着你在一组物品中只能选择一个

    物品,求花费小于等于m能得到的最大价值。

    ◦ Size之和小于等于1000,m<=1000

    区别不大。
    memset(f,-0x3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++)
        for(int j=m;j>=0;j--)
            for(int k=1;k<=size[i]&&w[i][k]<=j;k++)
                f[j]=max(f[j],f[j-w[i][k]]+v[i][k]);
    

    二维背包问题

    都差不多,只是多了一维而已(略)

    背包问题的解题思路

    对于一般的背包问题

    ◦做背包问题最关键的就是找清楚并反问自己?

    ◦这题里面 什么是容量? 什么是物品? 什么是物品的费用? 什么是 物品的价值?

    ◦容量,就是这题当中我们怎样表示状态的数组。

    ◦费用,就是用来f[i]---->f[i+v[k]],状态转移的跨度。

    ◦价值,就是你这个dp的数组,所维护的东西。维护的数值!

    ◦背包dp一定要理解好这三点,因为很多时候题目中的“费用”并非背包dp中的“费用”

  • 相关阅读:
    获取dbf中的表名
    dbf 工程模式连接(vfp c# )
    SQL Server插入中文数据出现乱码问题
    给老婆写的带返回的2048(数据库存储)
    BundleConfig包含js,css失败
    (wp8.1开发)添加数据(SQLite)库到app
    (wp8.1开发)触摸键从推出变返回
    java基础-jdk工具包
    java基础-开发工具IDEA
    java高级-动态注入替换类Instrumentation
  • 原文地址:https://www.cnblogs.com/Akaina/p/11251279.html
Copyright © 2020-2023  润新知