洛谷P2340 奶牛会展

题目背景

奶牛想证明它们是聪明而风趣的。为此,贝西筹备了一个奶牛博览会,她已经对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;
}
原文地址:https://www.cnblogs.com/yanshannan/p/7375739.html