• 0/1背包问题(动态规划)


    0/1背包问题:

    现有n种物品,对1<=i<=n,已知第i种物品的重量为正整数Wi。价值为正整数Vi,背包能承受的最大载重量为正整数W。现要求找出这n种物品的一个子集,使得子集中物品的总重量不超过W且总价值尽量大。

    (注意:这里对每种物品或者全取或者一点都不取,不同意仅仅取一部分)

    依据问题描写叙述。能够将其转化为例如以下的约束条件和目标函数:


    于是,问题就归结为寻找一个满足约束条件(1),并使目标函数式(2)达到最大的解向量。

    0-1背包问题能够看作是寻找一个序列,对任一个变量 的推断是决定=1还是=0.在推断完之后,已经确定了。在推断时,会有两种情况:

    (1)          背包容量不足以装入物品i,则=0。背包的价值不添加;

    (2)          背包的容量能够装下物品i,则=1,背包的价值添加。

    这两种情况下背包的总价值的最大者应该是对推断后的价值。令表示在前i个物品中可以装入容量为j的背包的物品的总价值,则可以得到例如以下的动态规划函数:

          

         式(1)说明:把前面i个物品装入容量为0的背包和把0个物品装入容量为j的背包。得到的价值均为0.式(2)第一个式子说明:假设第i个物品的重量大于背包的容量。则装入第i个物品得到的最大价值和装入第i-1个物品得到的最大价值是同样的,即物品i不能装入背包中。第二个式子说明:假设第i个物品的重量小于背包的容量,则会有两种情况:(1)假设把第i个物品装入背包,则背包中物品的价值就等于把前i-1个物品装入容量为的背包中的价值加上第i个物品的价值;(2)假设第i个物品没有装入背包,则背包中物品的价值就是等于把前i-1个物品装入容量为j的背包中所取得的价值。显然,取二者中价值较大者作为把前i个物品装入容量为j的背包中的最优解。

         我们能够一步一步的解出我们所须要的解。

    第一步,仅仅装入第一个物品。确定在各种情况下背包能得到的最大价值;第二步,仅仅装入前两个物品,确定在各种情况下的背包能够得到的最大价值;依次类推。到了第n步就得到我们所须要的最优解了。

    最后,便是在容量为W的背包中装入n个物品时取得的最大价值。

    为了确定装入背包的详细物品,从的值向前寻找,假设>,说明第n个物品被装入了背包中,前n-1个物品被装入容量为的背包中;否则,第n个物品没有装入背包中,前n-1个物品被装入容量为W的背包中。依此类推,直到确定第一个物品是否被装入背包为止。由此,我们能够得到例如以下的函数:.


         依据动态规划函数,用一个的二维数组C存放中间变量。表示把前i个物品装入容量为j的背包中获得的最大价值。

    下面为一个样例

         如果最大容量M=10。物品个数N=3,物品大小w{3,4,5},物品价值p{4,5,6}。


    从背包容量为0開始,1号物品先试,0,1,2,的容量都不能放.所以置0,背包容量为3则里面放4.这样,这一排背包容量为4,5,6,....10的时候,最佳方案都是放4.假如1号物品放入背包.则再看2号物品.当背包容量为3的时候,最佳方案还是上一排的最价方案c4.而背包容量为5的时候,则最佳方案为自己的重量5.背包容量为7的时候,非常显然是5加上一个值了。加谁??非常显然是7-4=3的时候.上一排 c3的最佳方案是4.所以。

    总的最佳方案是5+49.这样.一排一排推下去。最右下放的数据就是最大的价值了。(注意第3排的背包容量为7的时候,最佳方案不是本身的6.而是上一排的9.说明这时候3号物品没有被选.选的是1,2号物品.所以得9.)

    代码:

    #include<iostream>
    #include<algorithm>
    #include <vector>
    #include<string.h>
    #include<ctype.h>
    #include<math.h>
    using namespace std;
    const int n=3;   //物品数量
    const int W=10;  //背包容量
    int c[n+1][W+1];
    void DP(int v[], int w[])
    {
    	int i,j;
    	for (int i = 1; i <= n; i++)
    	{
    		for (int j = 1; j <= W; j++)
    		{
    			if (w[i] > j)
    				c[i][j] = c[i-1][j];
    			else
    				c[i][j]=max(c[i-1][j-w[i]]+ v[i],c[i-1][j]);
    		}
    	}
    }
    int main()
    {
    	int w[4]={0,3,4,5};
    	int v[4]={0,4,5,6};
    	memset(c,0,sizeof(c));
    	DP(v,w);
    	for(int i=0;i<=n;i++)
    	{
    		for(int j=0;j<=W;j++)
    			printf("%-3d ",c[i][j]);
    		puts("");
    	}
    }

    执行结果如图:


    这里是用二位数组存储的,能够把空间优化。用一维数组存储。


    用c[0..j]表示,c[j]表示把前i件物品放入容量为j的背包里得到的价值。把i从1~n(n件)循环后,最后f[j]表示所求最大值。

    *这里c[j]就相当于二位数组的c[i][j]。

    那么,怎样得到c[i-1][j]和c[i-1][j-w[i]]+w[j]?(重点!思考)
    首先要知道。我们是通过i从1到n的循环来依次表示前i件物品存入的状态。即:for i=1..N
    如今思考怎样能在是c[j]表示当前状态是容量为j的背包所得价值。而又使c[j]和c[j-w[i]]+w[i]表示前一状态的价值?

    逆序!

    #include<iostream>
    #include<algorithm>
    #include <vector>
    #include<string.h>
    #include<ctype.h>
    #include<math.h>
    using namespace std;
    const int n=3;   //物品数量
    const int W=10;  //背包容量
    int c[W+1];
    void DP(int v[], int w[])
    {
    	int i,j;
    	for (int i = 1; i <= n; i++)
    	{
    		for (int j = W; j >=0; j--)
    		{
    			if(j>=w[i])
    				c[j]=max(c[j-w[i]]+ v[i],c[j]);
    			printf("%-3d ",c[j]);
    		}
    		cout<<endl;
    	}
    }
    int main()
    {
    	int w[4]={0,3,4,5};
    	int v[4]={0,4,5,6};
    	memset(c,0,sizeof(c));
    	DP(v,w);
    }












  • 相关阅读:
    3G 4G 5G中的网络安全问题——文献汇总
    成都Uber优步司机奖励政策(4月4日)
    北京Uber优步司机奖励政策(4月4日)
    滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(4月4日)
    北京Uber优步司机奖励政策(4月3日)
    滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(4月3日)
    北京Uber优步司机奖励政策(4月2日)
    成都Uber优步司机奖励政策(4月2、3日)
    滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(4月2日)
    Spark入门(Python版)
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/6763076.html
Copyright © 2020-2023  润新知