如何运送最多的货物(0-1背包)

来源:互联网 发布:175凌波城输出数据 编辑:程序博客网 时间:2024/04/27 20:02

题目:某快递每天能收到成千上万的物流单,每个物流单的重量不一。 一位货车司机开着货车(限载5吨,含5吨,不考虑限高),想要一次性拿走尽可能重的货物

以下是货物清单:
货物编号 货物重量(单位:kg)
1. ————-509
2. ————-838
3. ————-924
4. ————-650
5. ————-604
6. ————-793
7. ————-564
8. ————-651
9. ————-697
10. ————649
11. ————747
12. ————787
13. ————701
14. ————605
15. ————644

输出运送最接近5000kg时的货物单号。

01背包

话说有一哥们去森林里玩发现了一堆宝石,他数了数,一共有n个。 但他身上能装宝石的就只有一个背包,背包的容量为C。这哥们把n个宝石排成一排并编上号: 0,1,2,…,n-1。第i个宝石对应的体积和价值分别为V[i]和W[i] 。排好后这哥们开始思考: 背包总共也就只能装下体积为C的东西,那我要装下哪些宝石才能让我获得最大的利益呢?

OK,如果是你,你会怎么做?你斩钉截铁的说:动态规划啊!恭喜你,答对了。 那么让我们来看看,动态规划中最最最重要的两个概念: 状态和状态转移方程在这个问题中分别是什么。

我们要怎样去定义状态呢?这个状态总不能是凭空想象或是从天上掉下来的吧。 为了方便说明,让我们先实例化上面的问题。一般遇到n,你就果断地给n赋予一个很小的数, 比如n=3。然后设背包容量C=10,三个宝石的体积为5,4,3,对应的价值为20,10,12。 对于这个例子,我想智商大于0的人都知道正解应该是把体积为5和3的宝石装到背包里, 此时对应的价值是20+12=32。接下来,我们把第三个宝石拿走, 同时背包容量减去第三个宝石的体积(因为它是装入背包的宝石之一), 于是问题的各参数变为:n=2,C=7,体积{5,4},价值{20,10}。好了, 现在这个问题的解是什么?我想智商等于0的也解得出了:把体积为5的宝石放入背包 (然后剩下体积2,装不下第二个宝石,只能眼睁睁看着它溜走),此时价值为20。 这样一来,我们发现,n=3时,放入背包的是0号和2号宝石;当n=2时, 我们放入的是0号宝石。这并不是一个偶然,没错, 这就是传说中的“全局最优解包含局部最优解”(n=2是n=3情况的一个局部子问题)。 绕了那么大的圈子,你可能要问,这都哪跟哪啊?说好的状态呢?说好的状态转移方程呢? 别急,它们已经呼之欲出了。

我们再把上面的例子理一下。当n=2时,我们要求的是前2个宝石, 装到体积为7的背包里能达到的最大价值;当n=3时,我们要求的是前3个宝石, 装到体积为10的背包里能达到的最大价值。有没有发现它们其实是一个句式!OK, 让我们形式化地表示一下它们, 定义d(i,j)为前i个宝石装到剩余体积为j的背包里能达到的最大价值。 那么上面两句话即为:d(2, 7)和d(3, 10)。这样看着真是爽多了, 而这两个看着很爽的符号就是我们要找的状态了。 即状态d(i,j)表示前i个宝石装到剩余体积为j的背包里能达到的最大价值。 上面那么多的文字,用一句话概括就是:根据子问题定义状态!你找到子问题, 状态也就浮出水面了。而我们最终要求解的最大价值即为d(n, C):前n个宝石 (0,1,2…,n-1)装入剩余容量为C的背包中的最大价值。状态好不容易找到了, 状态转移方程呢?顾名思义,状态转移方程就是描述状态是怎么转移的方程(好废话!)。 那么回到例子,d(2, 7)和d(3, 10)是怎么转移的?来,我们来说说2号宝石 (记住宝石编号是从0开始的)。从d(2, 7)到d(3, 10)就隔了这个2号宝石。 它有两种情况,装或者不装入背包。如果装入,在面对前2个宝石时, 背包就只剩下体积7来装它们,而相应的要加上2号宝石的价值12, d(3, 10)=d(2, 10-3)+12=d(2, 7)+12;如果不装入,体积仍为10,价值自然不变了, d(3, 10)=d(2, 10)。记住,d(3, 10)表示的是前3个宝石装入到剩余体积为10 的背包里能达到的最大价值,既然是最大价值,就有d(3, 10)=max{ d(2, 10), d(2, 7)+12 }。好了,这条方程描述了状态d(i, j)的一些关系, 没错,它就是状态转移方程了。把它形式化一下:d(i, j)=max{ d(i-1, j), d(i-1,j-V[i-1]) + W[i-1] }。注意讨论前i个宝石装入背包的时候, 其实是在考查第i-1个宝石装不装入背包(因为宝石是从0开始编号的)。至此, 状态和状态转移方程都已经有了。接下来,直接上代码。

for(int i=0; i<=n; ++i){    for(int j=0; j<=C; ++j){        d[i][j] = i==0 ? 0 : d[i-1][j];        if(i>0 && j>=V[i-1])  d[i][j] >?= d[i-1][j-V[i-1]]+W[i-1];    } }

i=0时,d(i, j)为什么为0呢?因为前0个宝石装入背包就是没东西装入,所以最大价值为0。 if语句里,j>=V[i-1]说明只有当背包剩余体积j大于等于i-1号宝石的体积时, 我才考虑把它装进来的情况,不然d[i][j]就直接等于d[i-1][j]。i>0不用说了吧, 前0个宝石装入背包的情况是边界,直接等于0,只有i>0才有必要讨论, 我是装呢还是不装呢。(完)

自己的实现(java):

public class Knapsack01 {    public int maxValue(int N,int C,ArrayList<Goods> goods){        int[][] d=new int[N+1][C+1];//状态d(i,j)表示前i个宝石装到剩余体积为j的背包里能达到的最大价值        //两种状态 1)不选择goods[i];2)选择goods[i],则d[i][j]=max{goods[i].value+d[i-1][j-goods[i].weight],d[i-1][j]}        int i=1,j=1;        for(i=1;i<=N;i++){            for(j=1;j<=C;j++){                if(goods.get(i).weight<=j){                    d[i][j]=Math.max(goods.get(i).value+d[i-1][j-goods.get(i).weight],d[i-1][j]);                }                else                     d[i][j]=d[i-1][j];            }        }        //打印选择的Item        int rest_space=C;        for(int m=N;m>=1;m--){            if(rest_space>=goods.get(m).weight){                if(d[m][rest_space]-d[m-1][rest_space-goods.get(m).weight]==goods.get(m).value){                    System.out.print(m+"-");                    rest_space=rest_space-goods.get(m).weight;                }            }        }        return d[N][C];    }    public ArrayList<Goods> initial(int a[]){        ArrayList<Goods> goods=new ArrayList<>();        goods.add(new Goods(0,0));        for(int i=0;i<a.length;i++)            goods.add(new Goods(a[i], a[i]));        return goods;    }    public static void main(String[] args) {        Knapsack01 ks=new Knapsack01();        int N=15,C=5000;        int a[]={509,838,924,650,604,793,564,651,697,649,747,787,701,605,644};        ArrayList<Goods> goods=ks.initial(a);        int maxValue=ks.maxValue(N, C, goods);        System.out.println(maxValue);    }    private static class Goods{        private int value;        private int weight;        public Goods(int value,int weight) {            this.value=value;            this.weight=weight;        }    }}

参考资料:http://www.hawstein.com/posts/dp-knapsack.html

0 0
原创粉丝点击