zoukankan      html  css  js  c++  java
  • 背包的题后总结。

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

    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 // 不取,在这组物品上继续取,在这组上重新开始取
    分组背包的变式
  • 相关阅读:
    给a标签加样式的写法
    IE6、IE7下不支持overflowy:hidden;
    fontfamily:微软雅黑
    文字加下划线
    IE8下按钮与右边的距离比IE7和IE6的多了一倍
    在button(div)里设置背景图后,在IE6下背景图的高度被撑开了
    li中包含span,在IE6、IE7下会有3pxbug
    事件冒泡
    [LeetCode] Insert Interval 解题报告
    [LeetCode] Generate Parentheses 解题报告
  • 原文地址:https://www.cnblogs.com/Milkor/p/4250461.html
Copyright © 2011-2022 走看看