题意:
某物 i , 有两种属性值 s[i] 和 f[i] ,每件物品可取或不取,求最后S + F的最大值,同时保证S >= 0 且 F >= 0。(S 为取出的 s[i] 的总和, F为取出的 f[i] 的总和)
思路:
1、涉及到取与不取的问题,很自然地想到了背包。
“物品的价值”这个显而易见,但是“背包的容量”在哪里?我们可以把两个维度 T 和 S,任取一个当作“物品的价值”,另一个做“背包的容量”,而容量最大值自然是这个属性所有正数之和。
即dp[i]:当 f 属性之和为 i 时, s 属性之和的最大值。
2、至于负数问题, 将数组开成最大值的两倍即迎刃而解。
3、坑点:01背包第二重循环因 f[i] 的正负不同, 循环方向也不同。
反思:
代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 100 + 10;
const int MAXM = 100 * 1000 * 2 + 1000;
int dp[MAXM];
int n;
int f[MAXN], s[MAXN];
int maxf;
int solve()
{
dp[100000] = 0;
for(int i = 0; i < n; i++)
{
if(f[i] >= 0)
{
for(int j = maxf; j >= f[i]; j--)
{
if(dp[j - f[i]] != -INF)
{
dp[j] = max(dp[j], dp[j - f[i]] + s[i]);
//cout << "j: " << j << " dp[j]: " << dp[j] << endl;
}
}
}
else
{
for(int j = f[i]; j + f[i] <= maxf; j++)
{
if(dp[j - f[i]] != -INF)
{
dp[j] = max(dp[j], dp[j - f[i]] + s[i]);
//cout << "j: " << j << " dp[j]: " << dp[j] << endl;
}
}
}
}
int maxs = 0;
bool flag = false;
for(int i = 100000; i <= maxf; i++)
{
if(dp[i] == -INF) continue;
else
{
if(dp[i] >= 0 && i - 100000 + dp[i] > maxs)
{
maxs = i - 100000 + dp[i];
flag = true;
}
}
}
if(flag)
return maxs;
else return 0;
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
scanf("%d %d", &s[i], &f[i]);
if(f[i] > 0) maxf += f[i];
}
maxf += 100000;
for(int i = 0; i < MAXM; i++)
{
dp[i] = -INF;
}
printf("%d\n", solve());
}