• 小偷的智慧——最浅显易懂的动态规划


    从小偷入室行窃谈起:

        话说一小偷深更半夜去偷东西,带了一个背包,但是这个背包只能装下10kg的物品(这个小偷也是够笨的不整个大点的包),推开了房门,看到了什么?(这不是废话嘛,当然看到的全部都是贵重物品啊)。小偷发现房间没人,小偷暗喜这就好办了,接下来就是到处搜寻贵重物品,功夫不负有心人(貌似这句话放在这里不恰当啊),一共找到了5件贵重物品(这户人间真是穷啊),暂且叫做a、b、c、d、e吧,这五件物品有重量也有价值,分别如下:

      a b c d e
    重量 4kg 5kg 6kg 2kg 2kg
    价值 6 4 5 3 6

              小偷看着这五件贵重物品发愣了,自己背包只能装10kg,要怎么装才能让带走东西的价值最多呢?(不要想着用口袋再装一点,就是那么多限制)

    画个表格计算一下:

        话说这年头日子真难混啊,就连做个小偷也不容易啊(更何况学计算机的呢,不说了,说多了都是泪啊)。小偷拿出纸和笔开始计算了起来(可见这小偷是有备而来啊,这心机......)。这个小偷画了这样一个二维的表格:

      0kg 1kg 2kg 3kg 4kg 5kg 6kg 7kg 8kg 9kg 10kg
    不放物品                      
    放第一个物品a(4kg,6)                      
    放第二个物品b(5kg,4)                      
    放第三个物品c(6kg,5)                      
    放第四个物品d(2kg,3)                      
    放第五个物品e(2kg,6)                      

               题外话:给大家解释一下这个表格的含义:

                行(下面用i表示坐标):一共有6种选择(5件物品怎么多了一个选择?选择什么都不装嘛!),此件物品放不放进自己的背包取决于上个物品的选择(这个理解起来有难度,下面还会进行解读的,读完整篇文章你就会懂了。)

                列(下面用k表示坐标):0-10表示背包的能容纳的重量,小偷可以选择什么都不装(白偷一次,空手回去),但是最多也只能装10kg。

                行和列的交叉:处就是装在背包里面物品的价值,用f[i,k]表示背包装有物品的总价值,i表示哪一行,k表示背包能容纳的重量,记住这两个的含义下文还会用到,举例f[2,8]表示第2行(放第二个物品b背包容量只有第8列(8kg背包物品价值最大价值(当然坐标从0开始算起)

    先初始一下数组:

              这个步骤很简单,第0行(不放物品时,不管你的背包能装多少东西价值都为0)、第0列(背包能容纳的的总量为0,什么都装不了,背包的价值还是为0)全部都是0。下面是初始化后的状态:

      0kg 1kg 2kg 3kg 4kg 5kg 6kg 7kg 8kg 9kg 10kg
    不放物品  0  0  0  0  0  0  0  0  0  0  0
    放第一个物品a(4kg,6)  0                    
    放第二个物品b(5kg,4)  0                    
    放第三个物品c(6kg,5)  0                    
    放第四个物品d(2kg,3)  0                    
    放第五个物品e(2kg,6)  0                    

    开始放入a物品了哟:

               旁白:表格每行的填入是从左至右(也就是随着背包容量的增大来装东西)

               忙活了半天终于可以开工了,a物品到底要不要放进去取决于两个因素,第一是a有4kg重,只有背包大于等于4kg的时候才能装进去(也就是说当i=1,k<4时f[i,k]=0);第二是当背包的重量大于等于4kg时要不要把a放进去呢(全篇的重点就在这里);Put or not put,that's a question。读者此时应该会有疑惑,肯定是把a放进去啊,反正在放a之前什么都没有,当然这样考虑我也同意,只是我要说的是,这是一个算法问题,必须找寻到规律所在,一环扣一环。现在我们按照刚才说的两个因数填一下第1行(时刻记得坐标是从0开始的),背包小于4kg时的值与上一行相同,f(1,1)=f(0,1)=0,f(1,2)=f(0,2)=0,f(1,3)=f(0,3)=0。随着背包能容纳的重量增大,当k=4时(背包能容纳4kg),现在到了选择的时刻了,将a放进去跟不放相比较,哪个价值要大一些,哪个价值大就选择哪个。将a放进去后背包的价值为6,a不放进去背包的价值为0,这里就要思考这个0对应的是表格里面的那个0了,不放a就回到了上一行的状态(第0行,i=0),a的重量是4kg,此时背包的的容量是4kg,不放a要用背包现在的容量4kg减去a的重量4kg,得到的值为0,即k=0,回到了f[0,0]的状态。找放入该物品之前的状态是一个非常关键的问题。那就再试着找一个状态吧,当背包为能容纳10kg时,a放进去背包总价值是6,不放进对应的状态对应f[0,6]价值为0,所以选总价值大的那个,即放进去。

      0kg 1kg 2kg 3kg 4kg 5kg 6kg 7kg 8kg 9kg 10kg
    不放物品  0  0  0  0  0  0  0  0  0  0  0
    放第一个物品a(4kg,6)  0  0  0  0   6   6  6   6  6  6  6
    放第二个物品b(5kg,4)  0                    
    放第三个物品c(6kg,5)  0                    
    放第四个物品d(2kg,3)  0                    
    放第五个物品入e(2kg,6)  0                    

    开始放入b物品了哟:

              放b物品与放a物品一样了(可不要因为一样就随便看,这里才是重头戏),b物品放不放进背包取决于两个因素,第一b有5kg的总量,背包容量有没有达到5kg,第二背包能容纳5kg,b放不放进去。当0<k<5时,背包价值与上一行相同,k=5时,不放b背包的价值与同列的上一行相同,价值为6,如果要把b放进去,必须回到放b之前的状态,按照之前的方法找到放b之前的状态f[1,0],f[1,0]的值(即背包的价值)是0,放入b后价值是0+4(注意0),比不放b的价值6要小,所以选择不把b放进背包,同理,k=6,7,8时一样。当k=9的时候,这时需要注意啦:不放b的价值是6,放b进背包则首先回到放b之前的状态,即f[1,4]的价值,发f[1,4]的价值是6,将b放进去的价值是6+4=10,大于不放b的价值6,所以此时将b放进去,即f[2,9]=10

      0kg 1kg 2kg 3kg 4kg 5kg 6kg 7kg 8kg 9kg 10kg
    不放物品  0  0  0  0  0  0  0  0  0  0  0
    放第一个物品a(4kg,6)  0  0  0  0   6   6  6   6  6  6  6
    放第二个物品b(5kg,4)  0  0  0  0  6  6  6  6  6  10  10
    放第三个物品c(6kg,5)  0                    
    放第四个物品d(2kg,3)  0                    
    放第五个物品入e(2kg,6)  0                    

    开始放入c物品:

              放c(6kg,5)物品,道理同放b一样,0<k<6是,与同行的上一排相同,k=6时,不放c的价值和同列上一行相同,放c时先回到放c之前的状态f[2,0],价值是f[2,0]+5=5<6,此时选择不放c。当k=11时,不放c的价值是10,放c的价值是f[2,4]+5=11>10,所以选择把c放进去。

      0kg 1kg 2kg 3kg 4kg 5kg 6kg 7kg 8kg 9kg 10kg
    不放物品  0  0  0  0  0  0  0  0  0  0  0
    放第一个物品a(4kg,6)  0  0  0  0   6   6  6   6  6  6  6
    放第二个物品b(5kg,4)  0  0  0  0  6  6  6  6  6  10  10
    放第三个物品c(6kg,5)  0  0  0  0  6  6  6  6  6  10  11
    放第四个物品d(2kg,3)  0                    
    放第五个物品入e(2kg,6)  0                    

    放入d、e绘出表格:

      0kg 1kg 2kg 3kg 4kg 5kg 6kg 7kg 8kg 9kg 10kg
    不放物品  0  0  0  0  0  0  0  0  0  0  0
    放第一个物品a(4kg,6)  0  0  0  0   6   6  6   6  6  6  6
    放第二个物品b(5kg,4)  0  0  0  0  6  6  6  6  6  10  10
    放第三个物品c(6kg,5)  0  0  0  0  6  6  6  6  6  10  11
    放第四个物品d(2kg,3)  0  0  3  3  6  6  9  9  9  10  11
    放第五个物品入e(2kg,6)  0  0  6  6  9  9  12  12  15  15  15

     精华总结:

    不难得出这样的公式(这是核心,只要理解了公式,写程序会迎刃而解,上面的废话也不用再看了

    f[i,k]=  0(i=0或k=0)    //背包不能装东西,或者没有东西可以装  

    f[i,k]=  f[i-1,k] (k<Wi)  //Wi表示第i个物品的重量

    f[i,k]=  max{f[i-1,k],f[i-1,k-Wi]+pi}  (k>=Wi)  //pi表示第i个物品的价值

    这年头做个小偷真难啊,还得算那么多东西!

     下面是java代码:

    import java.util.ArrayList;
    import java.util.Scanner;
    
    public class Main {
    
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入背包能容纳的重量:");
            int w = scanner.nextInt();
            System.out.println("请输入物品件数:");
            int n = scanner.nextInt();
            int[][] tab = new int[n + 1][w + 1];// 结果表
            int[][] a = new int[n][2];// 物品的重量价值记录笔
            System.out.println("请分别输入" + n + "件物品的重量和价值:");
            for (int i = 0; i < n; i++) {
                for (int k = 0; k < 2; k++) {
                    a[i][k] = scanner.nextInt();
                }
            }
            System.out.println(n + "个物品的重量、价值分别是:");
            for (int i = 0; i < n; i++) {
                System.out.print("(" + a[i][0] + "kg," + a[i][1] + ")	");
            }
            System.out.println();
            for (int i = 1; i < n + 1; i++) {
                for (int k = 1; k < w + 1; k++) {
                    if (k < a[i - 1][0]) {
                        tab[i][k] = tab[i - 1][k];
                    } else {
                        tab[i][k] = max(tab[i - 1][k - a[i - 1][0]] + a[i - 1][1], tab[i - 1][k]);
                    }
                }
            }
            System.out.println("绘制的二维表格:");
            for (int i = 0; i < n + 1; i++) {
                for (int k = 0; k < w + 1; k++) {
                    System.out.print(tab[i][k] + "	");
                }
                System.out.println();
            }
        }
    
        private static int max(int a, int b) {
            return a > b ? a : b;
        }
    
    }

     

  • 相关阅读:
    iOS与H5交互时,去掉调用方法时产生的警告
    技术支持网址:Technical support
    iPhone X 适配
    iOS11 上拉刷新后没有动画出现,直接瞬间出来没有停顿
    要不要去柬埔寨工作,很纠结,但是工资我又无法拒绝
    iOS10 CAAnimationDelegate的适配
    Swift3GCD
    Alamofire4.0 在 CocoaPods无法更新的问题
    8行代码全屏滑动
    bootstrap table 前端分页的问题
  • 原文地址:https://www.cnblogs.com/xiaoxueyong/p/5975905.html
Copyright © 2020-2023  润新知