洛谷P2340 奶牛会展

来源:互联网 发布:中维软件下载 编辑:程序博客网 时间:2024/04/29 21:34

题目背景

奶牛想证明它们是聪明而风趣的。为此,贝西筹备了一个奶牛博览会,她已经对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;}