zoukankan      html  css  js  c++  java
  • DP:Cow Exhibition(POJ 2184)(二维问题转01背包)

                   

                      牛的展览会

      题目大意:Bessie要选一些牛参加展览,这些牛有两个属性,funness和smartness,现在要你求出怎么选,可以使所有牛的smartness和funness的最大,并且这两个和都不能为负值

      这一题很有意思,首先是这个问题是二维的,它包含两个属性,但是他有一个很重要的条件就是牛只能选一次,所以我们一开始就很容易想到用背包貌似可以求解,但是这一题没有办法直接用背包,因为没有直接给出价值和容量(他给了两个价值)。

      但是,我们稍微变换一下,这一题就可以做了,我们要把一个看成是价值,把另一个看成是容量(容量随着包(牛)的增加而增加,可以视为无限)。但是很不幸的是,这一题是有负值的,但是没有关系,因为这一题的两个价值都是有范围的,所以我们可以采用把背包的中点定在半个范围内就可以了(相当于+range/2),那么最终我们把一个在二维展开的问题(求横纵坐标的和的最大值)转化为了一维背包的问题,求解的过程是和01背包是一样的,在负值的时候稍微注意一下反转求解方向就可以了(但是这一题可以剪枝,等一下讲)。

      到目前为止我们先入为主讲明白了这一题可以用01背白做,但是为什么呢?其实我自己一开始做的时候是按照离散的思想把100个包按照f+s的大小从小到大排,然后想按照像3666那样做DP,但是这样是不行的,因为我们无法确定负值的时候是否放入包,这样就会很麻烦(其实我自己也不知道自己是怎么想的),那么在这一题,我为什么可以把f看成价值把s看成容量?其实我们把另一个看成价值也是没问题的,其实价值和容量是可以互换的,容量其实是无限的,我们只是把他的解的区间固定在一个位置上,然后相当于是定住一个量,来求另一个量的最大值,因为我们我们用的是背包,所以我们价值的那个量一定是完全求解的,题目要我们求最大值,所以我们只用求出价值的最大值,那么最优解一定在最后的全局解内出现。

      最后说一下裁枝的问题,我们可以看到,如果我们只用简单的背包去求,那么会浪费大量的时间在无用的背包赋值(比如在i=1的时候,1000以后的背包值根本不可能有意义),所以我们根据到第几个背包来逐步扩大范围,减少求解的范围,节约时间。

      

     1 #include <iostream>
     2 #include <functional>
     3 #include <algorithm>
     4 
     5 using namespace std;
     6 typedef struct _cows
     7 {
     8     int smart;
     9     int fun;
    10 }Cows_Set;
    11 
    12 static Cows_Set cows[101];
    13 static int dp[2 * 1000 * 100 + 10];
    14 
    15 void Search(const int);
    16 
    17 int main(void)
    18 {
    19     int n;
    20     while (~scanf("%d", &n))
    21     {
    22         for (int i = 0; i < n; i++)
    23             scanf("%d%d", &cows[i].smart, &cows[i].fun);
    24         Search(n);
    25     }
    26     return 0;
    27 }
    28 
    29 void Search(const int n)
    30 {
    31     //01背包处理二维情况,要把一个看成容量,另一个看成价值
    32 
    33     int root = n * 1000, max_range = root * 2 + 10, ans = 0xf0f0f0f0, tmp_max, tmp_min;
    34     fill(dp, dp + max_range, 0xf0f0f0f0);
    35 
    36     dp[root] = 0;//01背包,就是把这个位置定为0,其他地方都是"不合法"
    37 
    38     for (int i = 0; i < n; i++)
    39     {
    40         tmp_max = root + i * 1000 + cows[i].smart;//对区间剪枝
    41         tmp_min = root - i * 1000 + cows[i].smart;
    42         if (cows[i].smart >= 0)
    43         {
    44             for (int j = tmp_max; j >= tmp_min; j--)
    45                 dp[j] = max(dp[j], dp[j - cows[i].smart] + cows[i].fun);
    46         }
    47         else
    48         {
    49             for (int j = tmp_min; j <= tmp_max; j++)
    50                 dp[j] = max(dp[j], dp[j - cows[i].smart] + cows[i].fun);
    51         }
    52     }
    53 
    54     for (int i = root; i < max_range; i++)
    55         if (dp[i] >= 0)
    56             ans = max(ans, i - root + dp[i]);
    57 
    58     printf("%d
    ", ans);
    59 }

  • 相关阅读:
    Typescript中抽象类与接口详细对比与应用场景介绍
    html5手势操作与多指操作封装与Canvas图片裁切实战
    可编辑DIV与移动端软键盘兼容性问题汇总
    作业系统前端架构总结
    移动端滑屏全应用【四】移动端动画贞动画函数mTween封装
    移动端滑屏全应用【三】requestAnimationFrame的兼容与使用
    python2.7和python3.7的区别!
    通俗易懂地解释卷积
    傅里叶变换就是这么简单!
    大话傅里叶变换,通俗易懂!
  • 原文地址:https://www.cnblogs.com/Philip-Tell-Truth/p/4960678.html
Copyright © 2011-2022 走看看