• 动态规划总结(01背包 完全背包 多重背包)


    动态规划总结(01背包 完全背包 多重背包)

    一、学习资料

    1.UVA DP 入门专题
    2.夜深人静写算法(二) - 动态规划
    3.算法之动态规划
    4.什么是动态规划?动态规划的意义是什么?
    5.01背包问题和完全背包问题
    6.背包九讲

    二、练习题目

    1.01背包练习
    2.完全背包 多重背包练习
    3.UVA的部分题目

    三、模型总结

    (1)01背包问题

    1.模型大意

    有n件商品,每件商品仅有一件,并且每件商品有自己的价值v和重量w。现在有一个最大承载重量为m的背包,求解最多能装下价值为多少的商品。

    2.状态转移方程

    dp[j] = max(dp[j],dp[j-w[i]]+v[i])

    3.核心代码片

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

    4.习题小结

    1).HDOJ.2955
    结合了概率和独立事件的知识。对于每个银行,小偷有抢或者不抢两种选择,并且每个银行抢不抢,事件发生时独立的。两个独立事件共同发生的概率,为两个事件发生概率的乘积。这是需要注意的。 另外一点就是,在遇到一些概率的问题的时候,通常需要将其转换成其对立事件发生的概率。这样解决起来较为容易。

    2).HDOJ.3466
    题面本身就是一个简单的01背包问题但是关键在于本题却需要排序,原因是会影响到后续物品的选择。简单来说,破坏了动态规划的无后效性
    对于每个商品,尤其能够买的临界值qi和其本身的价格pi,即当手中的钱不够qi的时候,商人就不考虑把第i件商品卖给你了。 比如有以下商品:

    label item1 item 2
    q q1 q2
    p p1 p2

    其中 q1 – p1 < q2 – p2
    若想把这两件商品全部购买,则至少需要p1+p2的金钱。那么如果我们没有这么多金钱怎么办。这就要考虑到dp时候的状态转移了。
    在状态转移的时候,item1能从item2转移的状态区间是[min(q1+p2,m),m],同理,item2能从item1转移的状态区间是[min(q2+p1,m),m]。对于p1+q2或p2+q1,个人的理解是,先买了物品1花费p1,因为现在要对物品2进行状态转移,则需要钱的数量大于q2才可以,否则无法进行状态转移,即物品1的状态没有得到利用,因为我们要尽可能重复的利用这个区间。(p2+q1同理可得)
    故对于以上2件商品,我们要看p1+q2和p2+q1孰大孰小。再根据题目数据不难看出 p1 + q2 < p2 + q1。
    根据以上的叙述不难得出结论:要按qi-pi的差值升序排序,然再做01背包。那么疑问也油然而生:为什么平常的做01背包也没见排序呀? 这是因为普通的01背包pi==qi,也就是说只要有足够的金钱,就能购买物品。故不需要排序。所以以后做这种类型的dp要留个心眼。

    3).HDOJ.2546
    这题跟上面的那个题目也有相似之处,由于此题恒定q为5,我们可以采用别的方法来处理。先按照价格降序/升序排序,然后把价格最大的那个留下来(留给那个剩余的5块钱,一边让剩余金额最小)。然后以总金额-5为背包容量,除去最贵的剩余菜为商品做01背包即可。

    4).HDOJ.2639
    此题本身依旧是一个01背包,但是题目要求输出的是第k最优解,平常题目都是直接输出最优解即可。
    根据背包九讲,不难想到肯定要增加一维,使得dp变为二维数组,其中第二维用来表示第K最优解。其次分别2个辅助数组a[],b[],容量以题目中的K为上限。实现方法如下:
    a.在做01背包的时候,多增加一层循环,由1->k的循环,用来储存第K个解。将原本的01背包状态转移方程中较大的放到a[]中,较小的放到b[]中。
    b.其次对a[],b[]做处理,依次从这2个数组中取出较大的那个,放到dp[i][k].表示第k解。
    代码如下:

        for(int i = 1;i<=n;++i){
                for(int j =m;j>=w[i];--j){
                    int l;
                    for( l = 1;l<=k;l++){
                        a[l] = dp[j-w[i]][l]+v[i];
                        b[l] = dp[j][l];
                    }
                    a[l] = b[l] = -1;
                    int x,y,z; x = y= z = 1;
                    while(z<=k && (a[x]!=-1 || b[y]!=-1)){
                        if(a[x] > b[y]){ dp[j][z] = a[x];x++;}
                        else {dp[j][z] = b[y];y++;}
                        if(dp[j][z] != dp[j][z-1]) z++;
                    }
                }
            }

    5.总结

    1). 不要忘记初始化dp数组,和其他的辅助数组。
    2). 在求一些最大最小值,或者是否可以构成的时候,经常讲dp整个初始化为INF,或者将dp[0]初始化为0或者1,这个要具体情况具体分析。

    (2)完全背包问题

    1.模型大意

    有n件商品,每件商品有无数件,并且每件商品有自己的价值v和重量w。现在有一个最大承载重量为m的背包,求解最多能装下价值为多少的商品。

    2.状态转移方程

    dp[j] = max(dp[j],dp[j-w[i]]+v[i])

    3.核心代码片

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

    4.习题小结

    1).UVA.674
    经典的硬币组成问题,问某个金额的组成方案有多少种,注意当数据特别大的时候要开long long数组。
    2).HDOJ.2159
    这题是带个数限制的完全背包,那么最先想到的就是增加一维来表示个数。

        for(int i =1; i<=k; ++i) {
            for(int j = a[i].ence; j<=m; ++j)
                for(int l = 1; l<=s;++l)
                    dp[j][l] = max(dp[j][l],dp[j-a[i].ence][l-1] +a[i].exp);
        }

    5.总结

    总的感觉完全背包没有什么难点,基本上就是套状态转移方程即可。

    (3)多重背包问题

    1.模型大意

    有n件商品,第ai件商品有ci件,并且每件商品有自己的价值v和重量w。现在有一个最大承载重量为m的背包,求解最多能装下价值为多少的商品。

    2.状态转移方程

    **F[i,v] = max{F[i − 1, v − k ∗ Ci
    ] + k ∗ Wi
    | 0 ≤ k ≤ Mi}**

    3.核心代码片

    一般集合二进制优化和单调队列优化,转换为01背包问题

    4.习题小结

    二进制优化
    即用二进制来表示有限的状态。通俗点讲,就是用1,2,4,8,16……这样的数来表示任意一个有限数字。如3 = 2+1, 7=4+2+1 如果我们把物品的个数,拆分成1个2个4个……在一起的物品,那么就是相当于对这样的物品做出选择,即做01背包。 这样就由多重背包,转换成了01背包的问题。
    二进制优化代码:

        for(int i = 0 ;i<6;++i){
            for(int j =1; j<=c[i]; j<<=1){
                val[cnt] = j*(i+1);
                num[cnt++] = j;
                c[i]-=j;
            }
            if(c[i]>0){
                val[cnt] = c[i] * (i+1);
                num[cnt++] = c[i];
            }
        }

    5.总结

    一般大部分题目都是使用二进制优化转换成01背包问题,或者是使用单调队列优化,极大提高程序效率。

    (4)LCS问题 – 最长公共子序列

    1.模型大意

    给出2个字符串,求出这两个字符串的最长公共子序列的长度。

    2.状态转移方程

    if(c1[i] == c2[j]) dp[i][j] =dp[i-1][j-1]+1;
    else dp[i][j] = max(dp[i-1][j],dp[i][j-1]);

    3.核心代码片

        for(int i =1; i<=len1;++i){
            for(int j = 1; j<=len2;++j){
                if(c1[i] == c2[j]) dp[i][j] =dp[i-1][j-1]+1;
                else dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
            }
        }

    (5)LIS问题 – 最长上升子序列

    1.模型大意

    给出一个序列,求出他的最长上升子序列的长度。

    2.状态转移方程 + 核心代码片

       for(int i = 2; i<=n;++i){
            if(a[i]>dp[len]) dp[++len] = a[i];
            else{
                int pos = BS(dp,a,1,len,i);
                dp[pos] = a[i];
                }
            }
    
    int BS(int dt[],int t[],int left, int right,int i)
    {
        int mid;
        while(left<right){
            mid = (left+right)/2;
            if(dt[mid]>=t[i]) right = mid;
            else left = mid+1;
        }
        return left;
    }
  • 相关阅读:
    C#内存释放(垃圾回收)
    C#内存释放(垃圾回收)
    C# winform窗口打开特效及窗口位置居中
    QString 字符串操作
    qt exe文件添加图标
    Qt 多语言转换
    加密算法比较
    C语言中最常用标准库函数
    fprintfAndFscanf简单操作
    vs使用fscanf和fprintf错误警告处理
  • 原文地址:https://www.cnblogs.com/pengwill/p/7367142.html
Copyright © 2020-2023  润新知