zoukankan      html  css  js  c++  java
  • poj2184 Cow Exhibition【01背包】+【负数处理】+(求两个变量的和最大)

    题目链接:https://vjudge.net/contest/103424#problem/G

    题目大意:

    给出N头牛,每头牛都有智力值和幽默感,然后,这个题目最奇葩的地方是,它们居然可以是负数!!现在叫你求出其中的牛中,智力值总数和幽默感总数加起来最大的值,当然,智力值的总数必须大于等于零,幽默感总数也是。

    解题思路:

    转载于>>>大牛博客

    对于每头牛,我们有两种选择,要么选,要么不选。看到这,就会觉得跟01背包很像,但是有 2 个变量来描述此状态的特征,智力,幽默感,并且他们可能是负数的。 
    如果此题选择dp来解,我们要想办法把它转换成01背包。

    下面转换01背包的方法。 
    首先,下面的模板是 一维数组解背包问题 。

    dp[i] = max(dp[i],dp[i-w[i]]+v[i])

    首先,针对2个变量,智力,幽默感,我们定义i是前i头牛的智力总和,dp[i]存储的是此时智力总和为i时,幽默感总数的最大值。有人或许会问,题目叫我们求智力和幽默感的总数最大值啊,这dp最后得出的只是幽默感总数的最大值啊?没关系,求出整个dp后,将每个d[i] 和 i相加 看谁大就行了,别忘了,i就是智力总和。 

    这一步是锁定变量。

    开始下一步前,我们先来定义一下dp数组的大小吧。 
    最多有100头牛,每个牛的智力范围在 -1000 – 1000 之间, 
    那他们的总和就是 落在 -1000*100 – 100*1000这个区间了里,也就是我们的dp要开100*1000*2这么大了。然后模仿坐标系,在100*1000*2这些数里找一个原点,自然的,这个是对称的,很明显远点是100*1000也就是100000了。大于100000的,智力总和大于0,小于100000,智力总和小于0.

    当然,这么大的数组我们不可能每个都访问到,这就要做个标记了,既然这里是求最大值,我们就把数组初始化为一个非常小的整数,inf = -100000000.

    好了,接下来是关键的负数的处理了。负数的处理很明显和正数不同,但他们都是dp,只不过,处理方式发生了变化。

    s[i] 第i头牛的智力
    f[i]第i头牛的幽默感
    for i :1 to N    //表示第几头牛
        if(智力是负数){
            //处理
        }
        else if(智力是正数或者0){
            //处理
        }

    先是,正数,每头牛有选与不选,仿照01背包

    for(int v = 100*1000*2;v>=s[i];v--)
        if(dp[v-s[i]]>inf)
            dp[v] = max(dp[v],dp[v-s[i]]+f[i])

    如果知道01背包的优化,上面就很简单了,逆序循环,保证每一个状态都能访问到上一个状态。

    v = 5;
    dp [5] = max(dp[5],dp[5-s[i]]+f[i])

    s[i] > 0,5-s[i]很明显是小于 5的,此时,dp[5-s[i]]保存的还是上一个状态的值,所以我们可以放心大胆的用。

    然后是判断条件,dp[v-s[i]]>inf,记得一开始我们把表示智力值总和的数组的初始化为inf吗?如果,dp[v-s[i]]<=inf,这意味着没有这个智力值没有用到,也就是dp[v] 这个是没有dp[v-s[i]]这个状态,既然没有我们就无视。

     来看负数的

    for(int v = s[i];v<100*1000*2;v++)
        if(dp[v-s[i]]>inf)
            dp[v] = max(dp[v],dp[v-s[i]]+f[i])

    仔细看会发现,其实就循环条件不同,为什么这样既可以呢? 
    其实,可以参考01背包的空间优化和完全背包的空间优化。

    不管是正数还是负数,我们都推,dp[v]时都要保证,它是由上一个状态的得来的。对于正数而言,上一个状态的智力总和是比当前状态小的。相反,对于负数而言,上一个状态的智力总和是比当前状态大的,s[i] < 0,v-s[i] > v,如果我们还是逆序循环的话,推dp[v]时,比他大的d[v-s[i]]早就改变了,不是上一个状态,那我们的递推就出现问题了,针对这个问题,我们正向循环就可以了。

    最后,dp完后

    来一个循环

    ans = 0;
    for i:100*1000 to 100*1000*2     智力总和要正数嘛
        if(dp[i]>=0)
            ans = max(ans,i - 100*1000 + dp[i]);

    i-100*1000就等于此时的智力值总和了,因为我们让数组发生偏移了。

    AC代码如下:

    #include <stdio.h>  
    #include <string.h>  
    #include <algorithm>  
    using namespace std;
    
    int dp[200005];
    const int inf = 1 << 30;
    
    int main()
    {
        int n, s[105], f[105], i, j, ans;
        while (~scanf("%d", &n))
        {
            for (i = 0; i <= 200000; i++)
                dp[i] = -inf;
            dp[100000] = 0;
            for (i = 1; i <= n; i++)
                scanf("%d%d", &s[i], &f[i]);
            for (i = 1; i <= n; i++)
            {
                if (s[i]<0 && f[i]<0)
                    continue;
                if (s[i]>0)
                {
                    for (j = 200000; j >= s[i]; j--)//如果s[i]为正数,那么我们就从大的往小的方向进行背包  
                        if (dp[j - s[i]]>-inf)
                            dp[j] = max(dp[j], dp[j - s[i]] + f[i]);               
                }
                else
                {
                    for (j = s[i]; j <= 200000 + s[i]; j++)//为负数则需要反过来  
                        if (dp[j - s[i]]>-inf)
                            dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
                }
            }
            ans = -inf;
            for (i = 100000; i <= 200000; i++)//因为区间100000~200000才是表示的整数,那么此时的i就是之前背包中的s[i],如果此时dp[i]也就
                                              //是f[i]大于等于0的话,我们再加上s[i](此时为i),然后减去作为界限的100000,就可以得到答案  
            {
                if (dp[i] >= 0)
                    ans = max(ans, dp[i] + i - 100000);
            }
            printf("%d
    ", ans);
        }
        return 0;
    }

    2018-04-30

  • 相关阅读:
    python中的有趣用法
    python计算程序运行时间
    python OptionParser模块
    优酷界面全新改版
    python数值计算模块NumPy scipy安装
    IOS开发-通知与消息机制
    四川大学线下编程比赛第一题:数字填充
    矩形旋转碰撞,OBB方向包围盒算法实现
    【Cocos2d-x 粒子系统】火球用手指飞起来
    它们的定义AlertDialog(二)
  • 原文地址:https://www.cnblogs.com/00isok/p/8973078.html
Copyright © 2011-2022 走看看