[USACO]奶牛博览会(DP)
Description
奶牛想证明他们是聪明而风趣的。为此,贝西筹备了一个奶牛博览会,她已经对N头奶牛进行了面试,确定了每头奶牛的智商和情商。
贝西有权选择让哪些奶牛参加展览。由于负的智商或情商会造成负面效果,所以贝西不希望出展奶牛的智商之和小于零,或情商之和小于零。满足这两个条件下,她希望出展奶牛的智商与情商之和越大越好,请帮助贝西求出这个最大值。
Input Format
第一行:一个整数N,表示奶牛的数量,1 ≤ N ≤ 100
第二行到第N + 1行:第i + 1行有两个用空格分开的整数:Si和Fi,分别表示第i头奶牛的智商和情商,−1000 ≤ Si ≤ 1000,−1000 ≤ Fi ≤ 1000
Output Format
第一行:单个整数,表示情商与智商和的最大值。贝西可以不让任何奶牛参加展览,如果这样应该输出0
Sample Input
5
-5 7
8 -6
6 -3
2 1
-8 -5
Sample Output
8
Hint
(选择 1,3,4 号奶牛,此时智商和为−5 + 6 + 2 = 3,情商和为7 − 3 + 1 = 5。加入 2 号奶牛可使总和提升到10,不过由于情商和变成负的了,所以是不允许的)
Solution
这题很容易想到DP,乍一看有点像01背包,每个牛都有选或者不选,但是仔细分析会发现这样不行。
在转移的过程中很难考虑智商以及情商大于0,简单来说就是有后效性。
那么又看到情商或智商绝对值小于1000,答案最大只能为200000,想到可以使dp[i]表示答案为i的方案是否存在,
但也很难判断智商和情商大于0,所以不妨将答案拆开为2部分,即
dp[i]表示情商和达到i的最大智商为多少
这个地方很关键,理解了这道题就简单了,
情商和为负数的情况也要考虑所以数组下标要向右移一段距离即加上一个常数,
情商最大和为100000,数组就开200000(正负各100000)
然后发现变成了01背包,不过要注意情商是恰好为i而不是不是最大为i,所以开始要把dp数组初始化为负无穷,然后dp[0+M]=0(这里M为一个常数),普通的01背包是初始化为0,原因这里不展开
状态转移的时候,要对w[i]的正负分情况转移,为正倒着做,为负正着做
最后答案就为max{dp[i+M]+i},0<=i<=M且dp[i+M]>0,i为情商dp[i+m]为智商
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, m, w[110], v[110], dp[200010];
int main()
{
freopen("in.txt", "r", stdin);
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &w[i], &v[i]);
if (w[i] > 0) m += w[i];
}
memset(dp, -127 / 2, sizeof(dp));
dp[m] = 0;
m *= 2;
for (int i = 1; i <= n; ++i) {
if (w[i] > 0)
for (int j = m; j >= w[i]; j--)
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
else for (int j = 0; j <= m + w[i]; j++)
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
int Ans = 0;
m /= 2;
for (int i = 0; i <= m; ++i)
if (dp[i + m] > 0) Ans = max(Ans, dp[i + m] + i);
printf("%d\n", Ans);
return 0;
}