题目背景
奶牛想证明它们是聪明而风趣的。为此,贝西筹备了一个奶牛博览会,她已经对N 头奶牛进行
了面试,确定了每头奶牛的智商和情商。
题目描述
贝西有权选择让哪些奶牛参加展览。由于负的智商或情商会造成负面效果,所以贝西不希望出展奶牛的智商之和小于零,或情商之和小于零。满足这两个条件下,她希望出展奶牛的智商与情商之和越大越好,请帮助贝西求出这个最大值。
输入输出格式
输入格式:
• 第一行:单个整数N,1 ≤ N ≤ 400
• 第二行到第N + 1 行:第i + 1 行有两个整数:Si 和Fi,表示第i 头奶牛的智商和情商,−1000 ≤ Si; Fi ≤ 1000
输出格式:
输出格式
• 单个整数:表示情商与智商和的最大值。贝西可以不让任何奶牛参加展览,如果这样做是最好的,输出0
输入输出样例
输入样例#1:
5
-5 7
8 -6
6 -3
2 1
-8 -5
输出样例#1:
8
说明
选择第一头,第三头,第四头奶牛,智商和为−5+6+2 = 3,情商和为7−3+1 = 5。再加
入第二号奶牛可使总和提升到10,不过由于情商和变成负的了,所以是不允许的
【题解】
此题为特殊的0-1背包问题。我们可以以情商为物品容量,智商为价值,将其转为正规的01背包问题。(楼下题解认为此方法未看到本质,但这种方法也能AC,但也可能是我造化太浅)
一点小优化(状压 DP):数组的下标和值都可以存储信息,所以我们可以把智商存在下标上,情商存在值上。
最后要注意:C++ 中没有负数下标,所以我们需要把 dp 数组平移 m(背包容量、为正数情商之和) 位。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int w[105]={},f[105]={},ff[200010]={};
int main()
{
int n,m=0;
//freopen("smrtfun.in","r",stdin);
//freopen("smrtfun.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&w[i],&f[i]);//w数组存智商,f数组存情商(w变下标,f变值)
if(w[i]>0) m+=w[i];//计算背包容量(情商)
}
m*=2;//m是平移的位数
memset(ff,-127/3,sizeof(ff));
ff[m/2]=0;
for(int i=1;i<=n;i++)
{
if(w[i]>0)//根据w[i]的符号确定循环方向,消除后效性,并从这里开始数组平移,防止出现负下标
for(int j=m;j>=w[i];j--)//接下来是01背包问题模板,保证每件物品只选一次
ff[j]=max(ff[j],ff[j-w[i]]+f[i]);
else
for(int j=0;j<=m-w[i];j++)//这个循环是数组平移弄出来的,由上个循环经数学运算得来。上个循环是m->w[i],两边乘-1,变成-w[i]**<-**-m,再加m,即得到m-w[i]**<-**0
ff[j]=max(ff[j],ff[j-w[i]]+f[i]);
}
int ans=0,k=m/2;//k是平移的位数
for(int i=0;i<=k;i++)
if(ff[i+k]>=0 && i+ff[i+k]>ans)//ff[i+k]存的是情商,必须判断非负数,而从0开始的下标保证智商没负数
ans=i+ff[i+k];////i是智商,ff[i+k]是情商
printf("%d",ans);
//fclose(stdin);
// fclose(stdout);
return 0;
}