每天一道算法题(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];
}




原文地址:https://www.cnblogs.com/engineerLF/p/5392979.html