动态规划-背包问题总结

01背包问题:

                       01背包其实是一个递推问题,每次都是最优解不断推出最终结果。一维和二维都要掌握。

动态方程:    

                   F[ i , v ] = max { F[ i−1 , v ] , F[ i−1 , v−Ci ]  +  Wi } ,意思是:当选第 i 个物品时 F [ i-1 , v ] 代表不放第 i 件物品,F [ i-1 , v - Ci ] + Wi  ; 代表放第 i 件物品,第 i-1 件物品得到的最优解加上第 i 件物品的价值。可以用一维数组  F[ i ] = max (F[ i ] ,F[ i - ci ]+wi ) ;

代码(二维数组):

memset(dp,0,sizeof(dp)) ;
for(int i=1 ;i<=n ;i++) // 最终答案为f[n][c]
{
    scanf("%d%d",&v,&w) ; // 边输入边计算
    for(int j=0 ;j<=c ;j++) // 这一行中 j 不能初始化为 v 因为如果这样 i 时就记录(得)不到
    {                      // i-1 时已经得到的最优解
        dp[i][j]=(i==1 ? 0 : dp[i-1][j]) ;
        if(j>=v&&dp[i][j]

代码(一维滚动数组):

memset(dp,0,sizeof(dp)) ;
for(int i=1 ;i<=n ;i++)
{    
    scanf("%d%d",&v,&w) ;    
    for(int j=C ;j>=v ;j--) // 这一行可以让 j 大于等于 v 因为结束后f[]数组     
       if(dp[j]

题目类型:

               1. 裸题. 2. 有的只给一个价值,只要把价值同时看成体积就可以了. 3. 有的是求一些物品平分问题(可以用搜索).4. 让求恰好装满(理解:初始化的 F 数组事实上就是在没有任何物品可以放 入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为 0 的背包可以在什 么也不装且价值为 0 的情况下被“恰好装满”,其它容量的背包均没有合法的解,属于 未定义的状态,应该被赋值为 -∞ 了。如果背包并非必须被装满,那么任何容量的背包 都有一个合法解“什么都不装”,这个解的价值为 0,所以初始时状态的值也就全部为 0 了).5.让打印路径。

完全背包:

                完全背包只学习了一种方法,二进制方法和多重背包一样。

动态方程:  

               (1) 转化为01背包 f [ i ][ v ] = max { f[ i - 1 ][ v - k*c[ i ] ] + k*w[i] ] { 0<=k*c[i]<=v }

               (2) F[ i ] = max ( F[ i ],F[ i - ci ] + wi ) ; 和 01 背包中的一维数组的一样只是枚举的顺序不同,这里讲一下枚举的循序(背包九讲):首先想想为什么 01 背包中要按照 v 递减的次序来循环。 让 v 递减是为了保证第 i 次循环中的状态 F[i,v] 是由状态 F[i−1,v −Ci] 递推而来。 换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入第 i 件物品”这件策 略时,依据的是一个绝无已经选入第 i 件物品的子结果 F[i−1,v −Ci]。而现在完全背 包的特点恰是每种物品可选无限件,所以在考虑“加选一件第 i 种物品”这种策略时, 却正需要一个可能已选入第 i 种物品的子结果 F[i,v−Ci],所以就可以并且必须采用 v 递增的顺序循环。这就是这个简单的程序为何成立的道理。

多重背包:

              多重背包只学了二进制优化二维):

   在这之前,我空间好像转过一个背包九讲,现在我就只对  
   01背包和多重背包有点印象了  
 
   先说下 01 背包,有n 种不同的物品,每个物品有两个属性  
   size 体积,value 价值,现在给一个容量为 w 的背包,问  
   最多可带走多少价值的物品。  
  
   int f[w+1];   //f[x] 表示背包容量为x 时的最大价值  
   for (int i=0; i=size[i]; j++)  
          f[j] = max(f[j], f[j-size[i]]+value[i]);  
  
   如果物品不计件数,就是每个物品不只一件的话,稍微改下即可  
   for (int i=0; i 0) {  
            value[count] = c*v;  
            size[count++] = c*s;  
        }  
    }  
    现在用count 代替 n 就和01 背包问题完全一样了 

分组背包(背包九讲):

      有n件物品和一个容量为V的背包,第 i 件物品的费用是 Ci ,价值是 Wi , 这些物品被划分为 k 组,每组中的物品相互冲突,最多选一件,求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

算法:

这个问题变成了每组物品有若干中策略:是选择本组的某一件,还是一件都不选。也就是说设F[ k,v ] 表示前 k 组物品花费费用 v 能取得的最大权值,则有:

                    F[ k , v ] = max { F[ k-1 , v ] ,F[k-1,v-Ci ] +Wi } ;

同样可以优化为一维。假设有 k 组物品每组有 m 个物品:

for(int i=0 ;i=0 ;j--) // V 为总体积
    for(int k=0 ;k

开始一直不明白这样为什么能做到每组物品只选择一件,自己模拟了一下才恍然大悟,因为放同一组的时候体积是逆序放的,而且放同一体积时同一组的都放一次,这样就保证了每组物品最多选一件(可以不选择)。

变形:每组至少选择一件(例题:HDU 3033 )。

   如果这样初始化时就要注意了,只有第 0 组所有的情况是正确的(组数从 1 开始),so ~ > 其它赋值为负无穷。因为每组物品可以选择多件,因此要把第二层 for 循环与第三层for 循环互换一下。这样就可以保证每组物品可以选择多件。

代码~~>

推荐博客:01背包总结 建议先看背包九讲         

你可能感兴趣的:(ACM算法)