背包的题后总结。

首先是自由评述:这几天的背包问题

0 1包:逆序。结果由上一组元素刷过去的结果得来的。

 

 1 void ZeroOnePack(int val,int vol)
 2 {
 3 
 4     int i;
 5     for(i=V;i>=vol;i--)
 6     {
 7         if(dp[i-vol]+val>dp[i])
 8         {
 9             dp[i] = dp[i-vol]+val;
10         }
11     }
12 }
01背包

满 包:初始化的时候。这个有个好方法 -inf 其中const int inf = 1<<28-1;这样的。写在顶部

    结果符合的得重新附值。 dp[0]=0常见的。

    原理:明确的是这个值要足够小或者大。能导致过滤掉这个结果。

完全背包:顺序。注意这个复杂度只有N*V了。因为不是一个物品刷一层循环 而是一种物品刷一层循环。并不能用01包的原理去单纯解释。

 

 1 void ZeroOnePack(int val,int vol)
 2 {
 3 
 4     int i;
 5     for(i=vol;i<=V;i+)
 6     {
 7         if(dp[i-vol]+val>dp[i])
 8         {
 9             dp[i] = dp[i-vol]+val;
10         }
11     }
12 }
完全背包

 

 

多重背包:如果说是利用完全包的刷一层方法。并不好知道你的个数使用了多少来维护个数!。所以分完全包的情况vol*num>=V 也就是说在这个范围内你可以随意数量放置。(当然拉。必须是最优的。)而如果小于这个值 就用01包。 但是是用bin优化过的再进行01包。 这个时候是顺序还是逆序呢?是顺序的。理解可以用01包去里面。当初错误理解完全包的方法可以在这里得到好的解释。所以用一个变量k=1 来进行。最后还得进行一次num的01至于为什么 这个和bin 有关

 

 1 void MulPack(int val,int vol,int num)  
 2 {  
 3     if(val*num>=aver)  
 4     {  
 5         ComPack(val,vol);  
 6         return;  
 7     }  
 8    
 9     int k=1;  
10     while(k<num)  
11     {  
12         ZeroOnePack(k*val,k*vol);  
13  
14         num -= k;  
15         k *= 2;  
16     }
17     ZeroOnePack(num*val,num*vol); 
18 }
多重背包

 

混合背包:这种背包并不难,只要传入方式正确。以及注意满包问题。

 

 1 int max(int a,int b)
 2 {
 3     return a>b?a:b;
 4 }
 5 void OneZeroPack(int val,int vol)
 6 {
 7     int j;
 8     for(j=V;j>=vol;j--)
 9     {
10         dp[j] = max(dp[j],dp[j-vol]+val);
11     }
12 }
13 void ComPack(int val,int vol)
14 {
15     int j;
16     for(j=vol;j<=V;j++)
17     {
18         dp[j] = max(dp[j],dp[j-vol]+val);
19     }
20 }
21 void MulPack(int val,int vol,int num)
22 {
23     if(val*num>=V)
24     {
25         ComPack(val,vol);
26         return;
27     }
28     int k = 1;
29     while(k<num)
30     {
31         OneZeroPack(val,vol);
32         num -= k;
33         k *= 2;
34     }
35     OneZeroPack(val,vol);
36 }
上述三种在一起。

 

 

二维费用:说白了是添加一个数组。多一层循环。实际上经常和满包之类的在一起。这个时候注意初始化即可。另外,二维费用经常出现的是数目的限制。必须放入指定M个数目。这个时候往往就是满包问题了。由此可以拓展到最大n数量 最小n数量的问题只要最后查找一遍获取结果即可

     另外要注意的地方。拿watch movie 这题来讲吧。http://acm.hdu.edu.cn/showproblem.php?pid=3496

这就是一个二维费用的问题 一维是时间 二维是数目。注意题目要求the shop just sell M piece of movies (not less or more then)。指定M个数目,那么满包问题。满包就是初始化的时候无穷小或者无穷大来过滤结果。但是问题就在于如何初始化。dp[time][num]根据实际意义。这是一个数目上的满包。即 dp[j][0] = 0 而dp[j][i!=0] = -INF.这是容易思考的根据实际意义首先确定下来的就是状态描述是 在体积有j装了M 的数目下的最大价值.所以就第0个物品来讲。(初始化就是第0个物品)只有数目0才是有意义的。

 

1             for(j=0;j<=L;j++)
2             {
3                 for(k=0;k<=M;k++)
4                 {
5                     if(k==0){dp[j][k]=0;}
6                     else{dp[j][k]=-inf;}
7                 }
8             }
初始化

 分组背包(一):首先有组的概念。对于这个分组背包是一个组里只能取一个物品或者取0个物品。并且你必须知道的是。用二维来讲。划分的状态就是dp[i][j]前i组在体积为j下所能获得的最大价值。那么简单。就是在这个组里查找一个元素能使得放入后是最大的。那么必定多一个循环k。那么一维

//有N组
//针对不同的V
//C[i][k],W[i][k];

for(i=1;i<=N;i++)
{
    for(j=V;j>=0;j--)
    {  //体积可是会变的 在这个组里所以>=0
        for(k=1;k<=num[i];k++) 
        {//第一个组的个数循环没问题。用来访问的
            if(C[i][k]<=j)
            {
            
            dp[j] = max(dp[j],dp[j-C[i][k]]+W[i][k]);
            }
         //一开始是放入一个dp[j]代表的含义是上一层 之后就是含义就是比较大小了。
        } 
    }
}
分组背包的一维

 

 分组背包(二):仔细观察”分组背包的一维“ 你就能发现。其代码和01背包的如此相似。那我们把最内层的循环当做查找一个最优的解放入当前j(体积)的背包中。那么可以抽象出来 一个组的物品。其实可以代替上一个最优的物品。并且一个组的物品只能放一个。这便和01背包无异。只是你多了一层循环来查找那个最优的物品。然后放上这个最优的物品。而得到最好的解。其实这个最优的物品已经给取名叫做”泛化物品“。我想这是一个很好的解决放<=1个元素的物品的解决问题的思路。(如果没有组 你还不会吗?)。

 

分组背包(三):根据上述的认为一个组为一个最优物品。那么根据混合背包。我们也有混合分组背包问题。即有不同规则的组别。但是我们先解决分组背包的变式。一个经典的>=1的情况。对于>=1也就是说一定会在这个组别里取一个。对于这种问题。假如是在普通的背包问题(没有组别的)上你会如何解决?那么就是要避免不取的状态。也就是设定不取的状态是非法的(INF)也就是刷上这一层是INF的。这个和满包不同。满包的话。如果数目为0之类的初始化是可行的。其实是一个原理。其实这里的0组刷成0更符合常理。非法和合法状态 也就是说每次i++的时候我们都要在当前层刷一遍INF。鉴于分组背包数据量较多。我们常用的手段是边输入的同时边处理数据。这里就一个状态包就算了。这里的刷层是这样的。对于不同的体积先让第一个物品刷一层。因为物品只有一个。所以逆刷。然后让第二个物品接着刷一层。(其实就是组内01包的问题) 也就是第二层是物品 第三层是体积。这个和分组背包相反。因为分组背包。只能装一个或者不装。假如像上面那种刷法会导致物品放上2个或者3个。分组背包是在一个体积上寻找最优的一个。而非叠加。

伪代码:

 1 dp[i][j]
 2 //代表着处理了前i组在体积为j下所能获得的最大价值。
 3 memset(dp,0,sizeof(dp))//没什么大不了的时候就0吧。因为全是合法的
 4 for(i:1~N)
 5     for(j:V~0) //所以尾巴还要判断要>=j
 6         for(k:1~num[i])
 7             if(vol[k]>=j)
 8                      if(dp[i-1][j-vol[i]]+val[i]>dp[i][j])
 9                       //从上一组的状态取下来的嘛。且dp[i][j]保留了上个物品的决策。        
10                             dp[i][j] = this.
11 
12 
13 memset(dp,INF,sizeof(dp))
14 dp[0][j] = 0;
15 for(i:1~N)
16     for(k:1~num[i])
17         for(j:V~0)
18           //以上都分析过了。
19               if(vol[k]>=j)
20                     dp[i][j] = max(dp[i][j],dp[i][j-vol[k]]+val[k],dp[i-1][j-vol[k]]+val[k]);
21 
22 // 不取,在这组物品上继续取,在这组上重新开始取
分组背包的变式

 

posted @ 2015-01-26 15:28  Milkor  阅读(155)  评论(0编辑  收藏  举报