Luogu P2340 [USACO03FALL]Cow Exhibition G

题面

题意简述

\(n\) 个物品,每个物品有两个属性(为整数),要求从中选若干个物品,使得两属性的总和最大且任意一个属性的和不得小于 \(0\)

题意类似于背包,复杂度 \(W = 400 \times 1000\) 也符合 \(O(nW)\),就用背包的思想来考虑

惯用伎俩,选一个属性为重量,另一个属性为价值,

但是因为由负数重量的存在,所以在使用C++的时候就要将数组大小开到 \(800000\) ,下标 \(400000\) 代表重量为 \(0\)\(400000\) 以下是负数,以上是正数

这样两个属性的和以及各自的和就能清晰算出了

接下来就是做背包的问题了

对于重量为正数的时候,与普通的01背包相同即可,由大到小更新

对于重量为负数的时候,因为减去一个负数相当于加上一个正数,所以在做背包的时候要从最小向大更新

核心代码:

    for(int i=1;i<=n;++i) {
        if(w[i]>=0) {                                            // 重量为正数
            for(int j=800000;j>=w[i];--j)                        // 从大往小枚举
                f[j] = max(f[j],f[j-w[i]]+v[i]);
        } else {
            for(int j=0;j<=800000+w[i];++j) {                    // 重量为负数
                f[j] = max(f[j],f[j-w[i]]+v[i]);                 // 从小往大枚举
            }
        }
    }

最终答案就是

\[\max_{400000\leq i \leq 800000} f_i+i-400000\ \ (f_i\geq 0) \]

最终代码:

#include<bits/stdc++.h>
using namespace std;
char ch;int fl;
template<typename T>
inline T redn(T &ret) {                                        // 快读
    ret=0,fl=1,ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')fl=-1;ch = getchar();}
    while(ch>='0'&&ch<='9') {ret=ret*10+ch-'0';ch=getchar();}
    return ret=ret*fl;
}
int n;
int w[404],v[404],f[400000<<1+1];
int main() {
    redn(n);
    for(int i=1;i<=n;++i) redn(w[i]),redn(v[i]);
    memset(f,128,sizeof f);
    f[400000] = 0;
    for(int i=1;i<=n;++i) {
        if(w[i]>=0) {                                            // 重量为正数
            for(int j=800000;j>=w[i];--j)                        // 从大往小枚举
                f[j] = max(f[j],f[j-w[i]]+v[i]);
        } else {
            for(int j=0;j<=800000+w[i];++j) {                    // 重量为负数
                f[j] = max(f[j],f[j-w[i]]+v[i]);                 // 从小往大枚举
            }
        }
    }
    int ans = 0;
    for(int i=400000;i<=800000;++i) if(f[i]>=0) ans = max(ans,f[i]+i-400000);
    printf("%d",ans);
}
posted @ 2020-03-25 23:40  AxDea  阅读(137)  评论(0编辑  收藏  举报