poj2184 Cow Exhibition(处理带负数的01背包)

题意:要求从N头牛中选择若干头牛去参加比赛,假设这若干头牛的智商之和为sumS,幽默度之和为sumF现要求在所有选择中,在使得sumS>=0&&sumF>=0的基础上,使得sumS+sumF最大并输出其值.

思路:算是01背包的变形。由于出租下标不能为负数,我们可以将数组范围扩大,不再以0来区分正负数,而是换一个数来区分,这里取了1e5来区分正负数。(因为正负数的最大范围是100*1000),然后这题也不像常规的01背包一样明确的指出什么是背包容量,什么是价值。所以,这里应该需要我们自己来制定。这里取了s为背包容量,f为物品价值。
所以定义dp[i]表示s为i的时候f的最大值。
最后求解答案的时候遍历[100000,200000]的区间即可。

还有一点,由于负数的存在,所以01背包需要讨论,参考了以为大佬的博客内容。

在一般的01背包压缩空间的时候,体积的遍历是从大到小,因为dp[v]=max(dp[v],dp[v-c[i]]+w[i]),当前的dp[v]只取决于比自己小的dp[v-c[i]],所以从大到小遍历时每次dp[v-c[i]]和dp[v]都是上一次的状态。
如果体积为负v-c[i]>v,从大到小遍历dp[v-c[i]]是当前物品的状态,不是上一个,这样就会出错,解决的办法是从小到大遍历。

#include <iostream>
#include <cstdio>
#include  <algorithm>
#include  <cstring>
using namespace std;
const int maxn = 2e5 + 5;
const int inf = 0x3f3f3f3f;
int dp[maxn];
int n;
int a[105], b[105];
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d%d", &a[i], &b[i]);
    memset(dp, -inf, sizeof(dp));
    dp[100000]=0;//注意定义初始状态
    for(int i = 1; i <= n; i++) {
        if(a[i] < 0) {//分类讨论
            for(int j = a[i]; j - a[i] <= 2e5; j++) {
                dp[j] = max(dp[j], dp[j - a[i]] + b[i]);
            }
        } else {
            for(int j = 2e5; j >= a[i]; j--) {
                dp[j] = max(dp[j], dp[j - a[i]] + b[i]);
            }
        }
    }
    int ans = 0;
    for(int i = 1e5; i <= 2e5; i++) {//遍历1e5以上的i值寻找答案
        if(dp[i] > 0) {
            ans = max(ans, i + dp[i] - 100000);
        }
    }
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/80435142