洛谷 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;
}

浙公网安备 33010602011771号