现在的位置: 首页 > 综合 > 正文

POJ 2184 Cow Exhibition 01背包

2014年01月17日 ⁄ 综合 ⁄ 共 1066字 ⁄ 字号 评论关闭

题意就是给出n对数

每对xi, yi 的值范围是-1000到1000

然后让你从中取若干对

使得sum(x[k]+y[k]) 最大并且非负   且 sum(x[k]) >= 0 sum(y[k]) >= 0  其中 k为所有你取到的标号

然后刚开始没什么思路

后来想想。 这就是背包吧。

将x看成花费,y看成价值

然后dp[i]表示在花费了i情况时价值最大是多少

注意到数值有负数

所以要加一些技巧

所有x的总值可能是-10w,而所有y的总值也可能是-10W

那么可以dp[100000] = 100000

以这个作为什么都没取的状态。

然后我们要判断第一个数的和是否非负就是判断 dp[i]中的i是否小于10W

然后判断第二个数的和非负就是判断dp[i]是否小于10W

这样的好处是我们只需要在转移方程时,

如果花费为c,价值为w

判断dp[i - c] 是否为0即可判断其状态是否合法

这是因为我们要的是恰好总和为i的状态。

所以起始的合法状态只有1个

然后在转移的时候也需要注意

开一个数组就行了。

传统的01背包转移, 使用滚动数组的方法是倒着来

那么对于这题有正花费有负花费。

所以对于负花费很显然是正着来的。

而且这题直接枚举1到20W这样花费 就比较慢。

因为有很多状态的花费还没有用到。

所以每次计算一下可能用到的间隔。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <map>
#include <vector>
#define MAXN 200005
#define INF 1000000007
using namespace std;
int dp[MAXN];
int mid = 100002;
int n;
int main()
{
    dp[mid] = mid;
    int c, w;
    scanf("%d", &n);
    int l = mid, r = mid;
    for(int i = 1; i <= n; i++)
    {
        scanf("%d%d", &c, &w);
        l = min(l, l + c);
        r = max(r, r + c);
        if(c > 0)
        {
            for(int j = r; j >= l; j--)
                if(dp[j - c])
                    dp[j] = max(dp[j - c] + w, dp[j]);
        }
        else
        {
            for(int j = l; j <= r; j++)
                if(dp[j - c])
                    dp[j] = max(dp[j - c] + w, dp[j]);
        }
    }
    int res = 0;
    for(int i = mid; i < MAXN; i++)
        if(dp[i] >= mid)
            res = max(res, i - mid + dp[i] - mid);
    printf("%d\n", res);
    return 0;
}

抱歉!评论已关闭.