zoukankan      html  css  js  c++  java
  • 动态规划——背包问题

    http://hawstein.com/posts/dp-knapsack.html

    话说有一哥们去森林里玩发现了一堆宝石,他数了数,一共同拥有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的背包中的最大价值。状态好不easy找到了。 状态转移方程呢?顾名思义,状态转移方程就是描写叙述状态是怎么转移的方程(好废话!

    )。 那么回到样例。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開始编号的)。至此。 状态和状态转移方程都已经有了。

    //volume[]宝石体积数组,worthness[]宝石价值数组,n宝石种类个数。C背包体积
    //状态d(i,j)表示前i个宝石装到剩余体积为j的背包里能达到的最大价值
    int  Knapsack(int volume[],int  worthness[],int n,int C)
    {
    	int maxworth;
    	int **d=new int*[n+1];
    	for (int i=0;i<=n;i++)//前n个相应第n-1下标元素
    	{
    		d[i]=new int[C+1];
    	}
    	for (int j=0;j<=C;j++)
    	{
           for (int i=0;i<=n;i++)
           {
    			 if (i==0)
    			 {
    				d[i][j]=0;
    			 }
                   else
    			   {
    				   d[i][j]=d[i-1][j];//默认不取第i个宝石
    			   }
                   if (i>0&&j>=volume[i-1])
                   {//注意讨论前i个宝石装入背包的时候, 事实上是在考查第i-1个宝石装不装入背包(由于宝石是从0開始编号的)。
    				   maxworth=d[i-1][j-volume[i-1]]+worthness[i-1];//取第i个宝石
    				   if (maxworth>=d[i][j])//取价值最大的
    				   {
                           d[i][j]=maxworth;
    				   }
                   }
    		 }
    	 }
    	int *uesd=new int[n];//标记是否拾起
    	for (int i=0;i<n;i++)
    	{
             uesd[i]=0;//初始时未拾起
    	}
    	int j = C;
    	for(int i=n; i>0; --i){
    		if(d[i][j] > d[i-1][j])//第i个拾起
    		{
    			uesd[i-1] = 1;
    			j = j - volume[i-1];
    		}
    	}
    	for(int i=0; i<n; ++i)  printf("%d ", uesd[i]);
    	 return maxworth;
    }


    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    SQL 实现月度留存率/复购率
    Mac安装mysql数据库,并用navicat链接
    MAC电脑安装git
    form 表格提交
    幼稚从来都是相对的
    Vue 80端口无法使用,直接运行到1024问题
    iOS SDK framework 真机和模拟器合并步骤
    XCODE调试
    UN: Half of Refugee Children Do Not Go to School
    Vue界面传值逻辑
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4683650.html
Copyright © 2011-2022 走看看