洛谷 P2340 [USACO03FALL] Cow Exhibition G 题解

题目链接

洛谷 P2340 [USACO03FALL] Cow Exhibition G

思路分析

观察到这里智商、情商都可能为负数,且没有明确的代价、价值,而且它问的是智商和加情商和的最小值。注意到对于一个数组,除了它的元素,其下标也可以存储数字。我们不妨让智商为代价、情商为价值,当然反过来应该也行。于是题目就变成在 \(dp\) 数组中找到一个下标加元素值最小的元素。

这道题还是 0-1 背包,但由于智商可以为负,所以由题意下标范围变为 \([-4\times 10^5,4\times 10^5]\)。可是下标应该是个正数啊。所以我们要想办法让 \([-4\times 10^5,0)\) 的这一部分小标变为正数。在观察到只需将每个下标加上 \(4\times 10^5\),相对位置不变,且都非负了。

所以我们定义 \(dp_j\) 表示智商和为 \(j-4\times 10^5\) 时情商和的最大值。则状态转移方程与普通 0-1 背包相似,不再赘述。注意到情商和也可能是负的,所以应该让 \(dp_{4\times 10^5}=0\)(智商为 \(0\) 可以不选,情商也为 \(0\)),其它初始化为负无穷。

最终求答案时,只需在智商和为正,即 \(i\ge 4\times 10^5\) 部分寻找 \(j-4\times 10^5+dp_j\),即智商和加情商和的最小值即可。时间复杂度 \(O(n\sum{|s_i|})\)

代码呈现

#include<bits/stdc++.h>
using namespace std;

const int N=405,M=800010,K=400000;
int n;
int s[N],f[N],dp[M];

int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;++i) scanf("%d%d",s+i,f+i);
    int m=0;
    for (int i=1;i<=n;++i) m+=abs(s[i]);
    memset(dp,-0x3f,sizeof dp);
    dp[K]=0;
    for (int i=1;i<=n;++i){
        if (s[i]>=0){
            for (int j=m+K;j>=s[i];--j) dp[j]=max(dp[j],dp[j-s[i]]+f[i]);
        }else{
            for (int j=0;j<=m+K+s[i];++j) dp[j]=max(dp[j],dp[j-s[i]]+f[i]);
        }
    }
    int ans=0;
    for (int i=K;i<=K+m;++i){
        if (dp[i]>=0) ans=max(ans,i-K+dp[i]);
    }
    printf("%d",ans);
    return 0;
}
posted @ 2026-01-11 12:01  CodingJuRuo  阅读(2)  评论(0)    收藏  举报