花了一晚上加一早上研究背包,唉一大把年纪了才狠下心弄dp也确实说不过去的......

背包入门当然还是看背包九讲(链接很多,没找到原作的,就随便贴一个链接了...),我再扯也是班门弄斧,只是贴一些摘要以及写代码时候的总结吧。

 

01背包:有N件物品和一个容量为V的背包。第i件物品的体积是v[i],价值是val[i],每种只有一件。求解将哪些物品装入背包可使价值总和max_val最大。

max_val[i][j] -->  dp[i][j] : 从前i个物品中选择重量不超过j的物品时的最大价值;

max_val[i][0] = max_val[0][j] = 0; ( i~{1,N}, j~{0,V}; )

只考虑第i件物品的策略
max_val[i][j] =  ① max_val[i-1][j];                      j > v[i]
            ② max{ max_val[i-1][j], max_val[i-1][j-v[i]]+val[i] }; j <= v[i];

②的判断过程:1) 计算不放入该物品时该重量的最大价值;
                    2) 计算当前物品的价值 + 可以容纳的剩余重量的最大价值;
                    3) 找到二者之中的最大值。
解决了所有的子问题之后,返回max_val[N][W]的值——N件物品重量为W时最大价值;

状态转移方程也可以用一维数组表示(切记01背包是逆序求解,稍后的完全背包是顺序求解):

1 void zero_one_pack(int cost, int value)  
2 {  
3     for(int i = sum;i >= cost;i--)  
4         dp[i] = max(dp[i],dp[i-cost]+value);  
5 }
6 for(int i = 1; i <= n; i++)
7 {
8     zero_one_pack(v[i], val[i]);
9 }    

 

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

基本跟01背包一个思路,只不过多了些变化:

max_val[i][j] -->  dp[i][j] : 从前i个物品中选择重量不超过j的物品时的最大价值;

max_val[i][0] = max_val[0][j] = 0; ( i~{1,N}, j~{0,V}; )

只考虑第i件物品的策略
max_val[i][j] =  ① max_val[i-1][j];                      j > v[i]
            ② max{ max_val[i-1][j-k*v[i]]+k*val[i] | k >= 0 };

                        =max{ max_val[i-1][j], max{max_val[i-1][j-k*v[i]]+k*val[i] | k >= 1} };

          =max{ max_val[i-1][j], max{max_val[i-1][(j-v[i])-k*v[i]]+ val[i] + k*val[i] | k >= 0} };

          =max{ max_val[i-1][j], max{max_val[i-1][(j-v[i])-k*v[i]] + k*val[i] | k >= 0} + val[i] };

              =max{ max_val[i-1][j], max_val[i][j-v[i]] + val[i] };

注意与01背包的区别--01背包的第一维是i-1,而完全背包的第一维是i;因此完全背包的一维数组表示时是顺序求解;

 1 void complete_pack(int cost,int value)  
 2 {  
 3     for(int i = cost; i <= sum; i++)  
 4         dp[i] = max(dp[i], dp[i-cost]+value);  
 5 }
 6     
 7 for(int i = 1; i <= n; i++)
 8 {
 9     complete_pack(v[i], val[i]);
10 }

 

多重背包:有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件体积是v[i],价值是val[i];
①基本算法

max_val[i][j] =  ① max_val[i-1][j];                      j > v[i]
            ② max{ max_val[i-1][j-k*v[i]]+k*val[i] | 0<=k<=n[i] };

                        =max{ max_val[i-1][j], max{max_val[i-1][j-k*v[i]]+k*val[i] | 1<=k<=n[i]} };

          =max{ max_val[i-1][j], max{max_val[i-1][(j-v[i])-k*v[i]]+ val[i] + k*val[i] | 0<=k<n[i]} };

          =max{ max_val[i-1][j], max{max_val[i-1][(j-v[i])-k*v[i]] + k*val[i] | 0<=k<n[i]} + val[i] };

              =max{ max_val[i-1][j], max_val[i][j-v[i]] + val[i] };

复杂度是O(V*Σn[i])


② 转化为01背包问题:把每种物品的n[i]件看成n[i]件01背包中不同的物品,则得到了物品数为Σn[i]的01背包问题,直接求解,复杂度也是O(V*Σn[i])

 

eg. hdu-2191

 1 scanf("%d %d",&n,&m);
 2 memset(dp,0,sizeof(dp));
 3 for(i=0;i<m;i++)
 4 {
 5     scanf("%d %d %d",&p,&h,&c);
 6     while(c--)
 7     {
 8         for(j=n;j>=p;j--)    dp[j]=max(dp[j],dp[j-p]+h);
 9     }    
10 }
11 printf("%d\n",dp[n]);
hdu_2191主要代码

相关文章: