码迷,mamicode.com
首页 > 其他好文 > 详细

无可救药的背尼玛和包尼玛

时间:2016-05-01 06:27:48      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:

ACM刷了一年多,背包学了无数次,学一次忘一次我也是醉了。

特此来纪念一下傻逼的我学习背包的结果。。。

顺便教教可爱又迷人的女朋友。

===========================尼玛,背了个包====================================

【包】:

    首先我们要来说一下这个包,背的这个包不是普通的包,因为普通的包不会这么恶心的(生无可恋)。。。

    这个包听说是个双肩包,不过我喜欢单肩包,这可能就是我记不住这个包的原因吧。。

    包有两个参数,一个是可装的总体积 V;还有一个就是可以获得的总重量 W(一般我们也把这个重量表示为价值);

    同时呢,我们往里塞的东西也是有相应的两个参数 v[ i ] , w[ i ] ;

    两个参数对应的就是它占用的体积 和 他带来的价值;

    好了,背景介绍完毕!

<  0 - 1 背了个包  >************************

   0-1 背包就是背包的精神内涵了(可怕.jpg)。

   【基本描述】: 给n个物品,每个物品有v[ i ],w[ i ];每个物品只有1个,问!这个包最大能装多少的价值;

   【掰扯掰扯】:傻逼一点的人肯定会想找性价比高的放,当然是不行的喽。反例自举。

          然后就诞生了0-1来背这个包;

          我们设 Dp[ i ] 表示:当背包的容量为 i 的时候,可以装入的最大的价值,(Dp[ i ]表示获得的最大价值);

          然后对于每一件物品(假设当前考虑的物品是第 j 件),我们枚举一下这件物品 放 还是 不放 到这个包里;

          ①:放

            因为我当前最大的容量是 i ,所以我放这个物品进去就要给它空出来它这么大的容量v[ j ],

            而且,空出来的这段容量所带来的价值是确定的,

            就是w[ j ],所以此时,我背包的总的价值就是 Dp[ i ] = Dp[ i - v[ j ] ] + w[ j ];

            (假设Dp[ i - v[ i ] ]是已知的);

          ②:不放

            不放就是不放,就没有什么好说的了,此时的最大的价值就是 Dp[ i ] 它原来的值了;

          ③:你到底放还是不放啊!

            当然是找两种方式中较大的一种喽。。

            原因很简单嘛,加入此时 i = V, 那么此时就是最后的结果了,我当然是要大的那个喽。

            其实更理论一点的说法是,动态规划 思维就是:通过子问题最优解得到全局的最优解,

            所以每一步我们都要得到最优解。

            然后我们的算法过程就很简单了:

                for(int j = 0; j < n; j++)
                {
                    for(int i = v; i >= v[j]; i--)
                    {
                        Dp[i] = max( Dp[i] , Dp[i-v[j]] + w[j] );
                    }
                }

          代码中有一些需要解释的是,第一层枚举我当前的物品 j ,第二层是从最大的容量开始考虑的,

          为什么呢,其实很简单,因为每个物品只能用一次,

           i - v[ i ] 是比 i 容量小的状态,如果我们从小往大的枚举的话,那么在 i - v[ j ] 的状态的时候,

          如果我们选择了放 j 进去,就意味着j 被用掉了,

          并且改变了Dp[ i - v[ j ] ] 的值,我们后来的枚举就不能保证只使用一次 j 了。

          倒着枚举可以避免这种状况的出现;

          [ 注 ]: 

            使用前要记得把Dp数组清空为 0 ;

          [ 问题变化 ]:

            ①:问你恰好装满的最大价值。

              解决这种问题的时候只要把Dp数组初始化改变一下,DP[ 0 ] = 0; 其余的Dp全部赋为 [ 负无穷 ]。

              [ 负无穷 ] 表示该种容量的时候不合法,这样在更新的时候,不合法的背包始终都会是负数,

              而合法的背包容量就是正数。

              最后判断一下Dp[ V ] 是否是负数就知道是不是合法的结果了。

 

 <  完全 背了个包  >************************

     完全背包就是0-1背包的简化版了。

    【基本描述】:给你n个物品,每个物品还是那两个参数,但是每一个都可以使用无限多次;

    【随便扯扯】:唯一的不同就是枚举的顺序,这次是从前往后的了。至于为什么不懂自己想。

              for(int j = 0; j < n; j++)
              {
                  for(int i = v[j]; i <= V; i++)
                  {
                      Dp[i] = max( Dp[i] , Dp[i-v[j]] + w[j] );
                  }
              }

           其它的东西都是一样的了,包括初始化和问题变种;

 <  多重 背了个包  >************************

     多重背包就是完全背包和0-1背包的合体版了(就是这么可怕)。

    【基本描述】:给你n个物品,每个物品还是那两个参数,但是每一个都可以使用的次数是给定的;

    【随便扯扯】:唯一的不同就是枚举的顺序,这次不是从前往后的了,也不是从后往前的,

           而是根据可用的数量,选择枚举的顺序。

           这个模板使用了二进制优化;不懂以后慢慢理解;

           为什么不写了呢,因为女朋友刚刚跟我生气来着,浪费了好长时间呢,所以不写了!

           这告诉我们,哄好女朋友才是提高效率的唯一途径,没有的就算了。

             #include<cstdio>
             #include<algorithm>
             #include<cstring>
            using namespace std;
            int Dp[60005];

            void ZeroOne( int cost ,int weight ,int sum )
            {
                for( int i = sum ; i>= cost ; i-- )
                    Dp[i] = max( Dp[i], Dp[i - cost] + weight );
            }
            void Complete( int cost ,int weight,int sum )
            {
                for( int i = cost ; i<= sum ; i++ )
                    Dp[i] = max( Dp[i], Dp[i-cost] + weight ); 
            }
            void Multi( int cost ,int weight, int count, int sum )
            {
                if( cost * count >= sum )
                    Complete( cost , weight, sum );
                else
                {
                    int i=1; 
                    while( i < count )
                    {
                         ZeroOne( i*cost , i*weight, sum );
                         count -= i ;
                         i<<=1 ; 
                    }
                    ZeroOne( cost* count , weight * count , sum);
                }
            }

    【进阶问题】:现在问题变了,物品还是原来的物品,现在我要问你能不能把在这个包装满,

           我不考虑物品的价值,只问你能不能装满。你说!能不能装满!

           其实都一样,就是个初始化的问题。。。

           不过今天做到BC一道题,题目要求取 K 个装满 V,这个。。还要思考一下行不行

          (其实就是下面要讲的这个 二维费用背包问题);

 <  二维费用 背了个包  >************************

     二维费用背包就是背包的升级版了。

    【基本描述】:给你n个物品,每个物品三个参数,包括两个费用参数,我们可以简单的理解为 va[ j ] 和 vb[ j ];

    【随便扯扯】:都说了是二维的了,此时的Dp当然就变成二维数组了,Dp[ U ][ V ] 表示这个包两种费用的最大承受值;

           那么对于一个物品,还是那样子,放还是不放,方放几个。。。也就是上面的几种DP方式;

           不同的是,对于状态转移的时候,要枚举两层了;

           

void ZeroOne(int cost_u,int cost_v, int weight, int u, int v)
{
    for(int i= u; i>=cost_u; i--)
        for(int j=v; j>=cost_v; j--)
            Dp[i][j] = max( Dp[i][j], Dp[i-cost_u][j-cost_v]+weight);
    
}

void Complete(int cost_u,int cost_v, int weight, int u, int v)
{
    for(int i= cost_u; i<=u; i++)
        for(int j=cost_v; j<=v; j++)
            Dp[i][j] = max( Dp[i][j], Dp[i-cost_u][j-cost_v]+weight);
    
}

void Multi(int cost_u,int cost_v, int weight,int count,int u,int v)
{
    if(cost_u * count >=u && cost_v * count >=v)
        Complete(cost_u, cost_v, weight, u, v);
    else
    {
        int i=1;
        while( i < count )
        {
            ZeroOne(cost_u*i, cost_v*i ,weight*i, u, v);
            count -= i;
            i<<=1;
        }
        ZeroOne(cost_u*count, cost_v*count, weight*count, u, v);
    }
}

明天继续.。。。。。。。。挂机ing

无可救药的背尼玛和包尼玛

标签:

原文地址:http://www.cnblogs.com/by-1075324834/p/5449956.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!