zoukankan      html  css  js  c++  java
  • 0—1背包归纳总结

    0-1背包问题: 

    有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

    这个问题的特点是:每种物品只有一件,可以选择放或者不放。

    算法基本思想:

    利用动态规划思想 ,子问题为:f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。

    其状态转移方程是:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}   这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。

    解释一下上面的方程:“将前i件物品放入容量为v的背包中”这个子问题,如果只考虑第i件物品放或者不放,那么就可以转化为只涉及前i-1件物品的问题,即:

    1、如果不放第i件物品,则问题转化为“前i-1件物品放入容量为v的背包中”;

    2、如果放第i件物品,则问题转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”(此时能获得的最大价值就是f [i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i])。则f[i][v]的值就是1、2中最大的那个值。

    (注意:f[i][v]有意义当且仅当存在一个前i件物品的子集,其费用总和为v。所以按照这个方程递推完毕后,最终的答案并不一定是f[N] [V],而是f[N][0..V]的最大值。)

    优化空间复杂度:

    以上方法的时间和空间复杂度均为O(N*V),其中时间复杂度基本已经不能再优化了,但空间复杂度却可以优化到O(V)。

    上面f[i][v]使用二维数组存储的,可以优化为一维数组f[v],将主循环改为:

    for i=1..N

    for v=V..0

    f[v]=max{f[v],f[v-c[i]]+w[i]};

    即将第二层循环改为从V..0,逆序。

    解释一下:

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

    当进行第i次循环时,f[v]中保存的是上次循环产生的结果,即第i-1次循环的结果(i>=1)。所以f[v]=max{f[v],f[v-c[i]]+w[i]}这个式子中,等号右边的f[v]和f[v-c[i]]+w[i]都是前一次循环产生的值。

    当i=1时,f[0..10]初始值都为0。所以

    f[10]=max{f[10],f[10-c[1]]+w[1]}=max{0,f[7]+4}=max{0,0+4}=4;

    f[9]=max{f[9],f[9-c[1]]+w[1]}=max{0,f[6]+4}=max{0,0+4}=4;

    ......

    f[3]=max{f[3],f[3-c[1]]+w[1]}=max{0,f[3]+4}=max{0,0+4}=4;

    f[2]=max{f[2],f[2-c[1]]+w[1]}=max{0,f[2-3]+4}=0;//数组越界?

    f[1]=0;

    f[0]=0;

    当i=2时,此时f[0..10]经过上次循环后,都已经被重新赋值,即f[0..2]=0,f[3..10]=4。利用f[v]=max{f[v],f[v-c[i]]+w[i]}这个公式计算i=2时的f[0..10]的值。

    当i=3时同理。

    具体的值如下表所示:


    因此,利用逆序循环就可以保证在计算f[v]时,公式f[v]=max{f[v],f[v-c[i]]+w[i]}中等号右边的f[v]和f[v-c[i]]+w[i]

    保存的是f[i-1][v]和f[i -1][v-c[i]]的值

    当i=N时,得到的f[V]即为要求的最优值。

    初始化的细节问题:

     在求最优解的背包问题中,一般有两种不同的问法:1、要求“恰好装满背包”时的最优解;

                                                                      2、求小于等于背包容量的最优解,即不一定恰好装满背包。

    这两种问法,在初始化的时候是不同的。

    1、要求“恰好装满背包”时的最优解:

    在初始化时除了f[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。如果不能恰好满足背包容量,即不能得到f[V]的最优值,则此时f[V]=-∞,这样就能表示没有找到恰好满足背包容量的最优值。

    2、求小于等于背包容量的最优解,即不一定恰好装满背包:

    如果并没有要求必须把背包装满,而是只希望价值尽量大,初始化时应该将f[0..V]全部设为0。

    总结

    01背包问题是最基本的背包问题,它包含了背包问题中设计状态、方程的最基本思想,另外,别的类型的背包问题往往也可以转换成01背包问题求解。故一定要仔细体会上面基本思路的得出方法,状态转移方程的意义,以及最后怎样优化的空间复杂度。

    0-1背包问题代码:

    int a[101],c[101],f[101][1001]={0};//f[i][j]表示前i棵草药,j时间内最优解 
    int max(int x,int y)
    {
    	if(x>y)   return x; 
    	else      return y;   
    }
    
    int main()
    {
    	int t,m;
    	freopen("medic.in","r",stdin);
    	freopen("medic.out","w",stdout); 
    	scanf("%d %d",&t,&m);
    	
    	for(int i=1;i<=m;i++)
    	   scanf("%d%d",&a[i],&c[i]); 
    	   
    	for(int i=1;i<=m;i++)//对草药棵树外层循环 
    	{
    		for(int j=1;j<=t;j++)//对时间t内层循环 
    		{
    			if(a[i]<=j)//第i棵草药能摘 
    			{
    				//取两种摘取情况的最优解 
    				f[i][j]=max(f[i-1][j],f[i-1][j-a[i]]+c[i]);
    			 
    			}
    			else //第i棵草药不能被摘 
    			    f[i][j]=f[i-1][j]; 
    		}
    	}
    	/*
    	  以上求解最优解f[i][j]时可优化为
    	   for(int i=1;i<=m;i++)
    	   {
    		 for(int j=t;j>=a[i];j--)
    		 {
    			f[j]=max(f[j],f[j-a[i]]+c[i]);
    		 }
    	   } 
    	*/
    	printf("%d",f[m][t]);//输出m棵草药在t时间内被摘取的最优解 
    	return 0;
    }

    0—1背包实现代码2

    int s[1005]={0};//s[T]表示T时间内采药的最大价值 
    int main()
    {
    	int T, M, i, j ,v, n;
    	freopen("medic.in","r",stdin);
    	freopen("medic.out","w",stdout); 
        scanf("%d%d",&T,&M);
        for ( i = 1 ; i <= M ; i++ )
        {
           scanf("%d%d",&n,&v);//输入的草药,时间为n,价值为v 
           for (j=T; j>=n; j--)
               if (s[j-n] + v > s[j])//判断s[j]取最优解时,要不要采草药n 
                   s[j] = s[j-n] + v;//最优时要采草药n,修改s[j]最优的值 
        }
        printf("%d
    ",s[T]);
        return 0;
    }
    



  • 相关阅读:
    C# 实现复杂对象的序列化与反序列化
    C#操纵XML文档(主要是应用程序的配置文件)
    滕王阁序——王 勃 (注:我至爱的一篇文章)
    SmartClient(智能客户端)学习笔记之——Smart Client基本学习资源
    listview按列自动排序的一点补充
    (转)SmartClient(智能客户端)学习笔记之——Microsoft Updater Application Block ApplicationUpdater assembly设计
    用超图实现城市给水的爆管分析
    .net2005中对asp.net中GridView的常用操作
    .net2003中对DataGrid的常用操作
    为DataGrid或者GridView或者DataList最前面增加一排序号
  • 原文地址:https://www.cnblogs.com/tham/p/6827483.html
Copyright © 2011-2022 走看看