题目:
题意:
n个元素,每个元素都有两种属性s[i]和f[i],满足abs(s[i]),abs(f[i])<=m,现在从中选取若干个元素,使得所有s[i]之和>=0,所有f[i]之和>=0,且最大化s[i]+f[i]的值;n<=100,m<=1000,你可以一个元素也不选
题解:
真是一道01背包的好题?
最大的累计值为100000,但可能有负,我们就要设计0~200000的
注意如下:
1、这种有两个量而且范围很大的可以一维当成数组下标,另一维当成累加的量,因为要求两个数分别的和都大于0,第二个数直接从大于0的范围内找
2、我们都知道s[i]为正数时01背包要从大到小循环,可以保证只选一次;如果s[i]为负数,01背包就要从小到大循环了,这样才能保证只选一次!(s[i]是数组下标!)
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int dp[200005],s[105],f[105];
int main()
{
int n,i,j;
while (scanf("%d",&n)!=EOF)
{
memset(dp,0x8f,sizeof(dp));
int Minn=dp[0];
dp[100000]=0;
for (i=1;i<=n;i++) scanf("%d%d",&s[i],&f[i]);
for (i=1;i<=n;i++)
{
if (s[i]<0 && f[i]<0) continue;
if (s[i]<0)
{
for (j=s[i];j<=s[i]+200000;j++)
if (dp[j-s[i]]>Minn) dp[j]=max(dp[j-s[i]]+f[i],dp[j]);
}
else
{
for (j=200000;j>=s[i];j--)
if (dp[j-s[i]]>Minn) dp[j]=max(dp[j-s[i]]+f[i],dp[j]);
}
}
int ans=0;
for (i=100000;i<=200000;i++)
if (dp[i]>=0) ans=max(ans,dp[i]+i-100000);
printf("%d\n",ans);
}
}