题目大意: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 }