• 每天一道算法题(34)——背包问题


    1.0-1背包问题

             有N件物品和一个能装质量为W的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的总重不超过背包总重,且价值总和最大。这个问题的特点是:每种物品只有一件,可以选择放或者不放。

            定义f[i][j]:在前i个物品中用容量为j的包选择所能得到的最大价值。则转移方程:f[i][j]=max{f[i-1][j],f[i-1][j-w[i]]+v[i]},v[i]和w[i]对应为第i件物品的价值和重量。


           代码,对于int[]  w={2,2,6,5,4};int[]  v={6,3,5,4,6};注意,求得的最优解为,小于等于背包容量所能获得的最高价值

    import java.util.Iterator;
    import java.util.Vector;
    
    public class bag {//背包
    	int maxWeight;
    	Vector<thing> things;//存储所选择的物品
    	int maxValue;
        bag(){this(0);}
        bag(int m){
        	maxWeight=m;
        	things=new Vector<thing>();
        	maxValue=0;
        }
        
        public void choose(thing[] t){//选择物品
        	if(t.length==0||maxWeight==0)
        		System.out.println("Invalid things array");
        	int[][] func=new int[t.length+1][maxWeight+1];
    
            int i,j; 
        	for(i=0;i<=maxWeight;i++)
        		func[0][i]=0;
        	
        	//最大价值计算
        	for(i=1;i<=t.length;i++)  	
        	    for(j=maxWeight;j>0;j--){
        	    	if(t[i-1].weight<=j&&(func[i-1][j-t[i-1].weight]+t[i-1].value)>func[i-1][j])
        	    		func[i][j]=func[i-1][j-t[i-1].weight]+t[i-1].value;
        	    	else
        	    		func[i][j]=func[i-1][j];
            }
        	
        	
        	//找出所选择的物品
        	j=0;
        	for(i=1;i<=maxWeight;i++)
        		if(func[t.length][i]>maxValue)
        			j=i;
        	i=t.length;
        	while(i>0&j>0){
        		if(func[i][j]!=func[i-1][j]){
        		   things.addElement(t[i-1]);
        		   j-=t[i-1].weight;
        		}
        		i--;
        	}
        	
        }
        
        public void show(){//输出物品
        	 Iterator<thing> iter=things.iterator();
        	 while(iter.hasNext())
        		 System.out.println(iter.next());
        }
    }
    
    class thing{//物品
    	int weight;
    	int value;
    	thing(){this(0,0);}
    	thing(int w,int v){weight=w;value=v;}
    	public String toString(){
    		return "物品"+Integer.toString(weight)+":"+Integer.toString(value);
    	}
    }
        注意:1.由于状态数作为矩阵标号,因此要求总重量或者容积必须为整数。

                   2.不需要找出所选择的物品时。可以将空间复杂度由O(NW)将至O(W),即满足条件更新即可。

                   3.算法的时间复杂度为O(NW),对应物品总数和最大重量

                   4.由于涉及状态转移,因此从后往前更新最好


    2.完全背包

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

              注意到完全背包使用了正序更新。因为每一件物品是无限的。所以在考虑“加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果。

    伪代码如下。其中空间爱你复杂度为O(v)时间复杂度为

       for (int i = 1;i <= N;i++)  
        {  
            for (int v = 1;v <= V;v++)  
            {  
                f[i][v] = 0;  
                int nCount = v / weight[i];  
                for (int k = 0;k <= nCount;k++)  
                {  
                    f[i][v] = max(f[i][v],f[i - 1][v - k * weight[i]] + k * Value[i]);  
                }  
            }  
        }

            完全背包问题有一个很简单有效的优化,是这样的:若两件物品i、j满足c[i]<=c[j]且w[i]>=w[j],则将物品j去掉,不用考虑。即,如果一个物品A是占的地少且价值高,而物品B是占地多,但是价值不怎么高,那么肯定是优先考虑A物品的。


    3.0-1背包问题的变种

             (1)设报考第i个学校需要花费v[i]美元,能够被录用的概率为w[i]。已知有一定的钱M,则应该如何花钱使得没有被录取的概率最小。

                 设f[i][j]:在前i个学校中花费j元没有被录取的概率。状态初始化为1。

                 则转移方程f[i][j]=min{f[i-1][j],f[i-1][j-v[i]]*(1-w[i])}

              (2)设银行i有现今v[i],小偷全部偷走时,被抓住的概率为p[i]。已知小偷最多想偷一定的钱,但希望以最大概率逃走。则应该去偷哪些银行。

                f[i][j]表示在前i个银行中偷得的money为j时能够逃脱的最大概率。状态初始化为0。

               状态转移方程:f[i][j]=max{f[i-1][j],f[i-1][j-v[i]]*(1-p[i])}

             (3)假设完全装满问题下的最优解,以0-1背包为例,要求最后的解的重量和为给定的重量。注意此时,除了0位置之外,初始化均为最小值。原因

                  1)对于承重为j的背包,f[0][j]无任何意义,即无法装满、

                  2)对于第一次f[i][j]不为负值的情况,要求当前所添加的物体直接把当前背包填满。此时用到了最开头的f[i][0]。而后不为负值的f[i][j],j=weight[i]+weight[pre].

    #include <iostream>
    #include <vector>
    using namespace std;
    const int MIN=0x80000000;
    const int N=3;   //物品数量
    const int V=5;  //背包容量
    int f[V+1];
    
    int Package(int *W,int *C,int N,int V);
    void main(int argc,char *argv[])
    {
     int W[4]={0,7,5,8};      //物品权重
     int C[4]={0,2,3,4};      //物品大小
     int result=Package(W,C,N,V);
     if(result>0)
     {
      cout<<endl;
      cout<<"the opt value:"<<result<<endl;
     }
     else
      cout<<"can not find the opt value"<<endl;
     return;
    }
    
    int Package(int *W,int *C,int N,int V)
    {
     int i,j;
     memset(f,0,sizeof(f));  //初始化为0
    
     for(i=1;i<=V;i++)               //此步骤是解决是否恰好满足背包容量,
      f[i]=MIN;                //若“恰好”满足背包容量,即正好装满背包,则加上此步骤,若不需要“恰好”,则初始化为0
        
     for(i=1;i<=N;i++)
         for(j=V;j>=C[i];j--)    //注意此处与解法一是顺序不同的,弄清原因
          {
           f[j]=(f[j]>f[j-C[i]]+W[i])?f[j]:(f[j-C[i]]+W[i]);
           cout<<"f["<<i<<"]["<<j<<"]="<<f[j]<<endl;
          }
         return f[V];
    }




  • 相关阅读:
    JSP基础学习(二)
    JSP基础学习(一)
    Android 使用开源xUtils来实现多线程下载(非原创)
    Android 多线程断点下载(非原创)
    Android HttpClient框架get和post方式提交数据(非原创)
    Android ListView使用(非原创)
    ScrollView 的使用(非原创)
    Android SQLite API的使用(非原创)
    Android SQLite的使用2(非原创)
    位运算
  • 原文地址:https://www.cnblogs.com/engineerLF/p/5392979.html
Copyright © 2020-2023  润新知