洛谷题单指南-动态规划2-P2340 [USACO03FALL] Cow Exhibition G
原题链接:https://www.luogu.com.cn/problem/P2340
题意解读:所选牛的智商、情商之和最大值,且智商之和、情商之和不能为负数
解题思路:
本题可以转化为01背包问题:
设背包的容量是最大的智商之和,此题智商之和的范围是-400000~400000
状态表示:设dp[i][j]表示前i头牛智商之和为j时的最大情商和, s[]、f[]存储智商、情商
状态计算:根据01背包dp[i][j] = max(dp[i-1][j], dp[i-1][j - s[i]] + f[i])
初始值:由于情商和可能为负,dp初始为memset(dp, -0x3f, sizeof(dp)),dp[0][0] = 0
结果:取dp[i] + i的最大值
以上分析不错,但是问题来了:
1、dp[i][j]中i最多400个,j最多800000个,内存爆了,所以要优化为一维
设dp[j]表示所选牛智商之和为j时的最大情商和,有dp[j] = max(dp[[j], dp[j - s[i]] + f[i])
2、j取值可能为负数,数组下标越界
可以将j整体加上400000,这样取值范围变成了0~800000
3、递推式中j依赖j - s[i],而s[i]可以正也可以负,所以j - s[i]可能比j大,也可能比j小
因为通过一维滚动数组实现,
当j依赖的j - s[i]比j小时,j必须从大到小遍历(先更新下标大的,避免依赖的值被覆盖)
当j依赖的j - s[i]比j大时,j必须从小到大遍历(先更新下标小的,避免依赖的值被覆盖)
4、j加了400000来保证不会是负值,结果如何计算?
枚举dp[400000] ~ dp[800000]
判断当dp[j] >= 0时,求dp[j] + j - 400000的最大值
结合上述分析,得到代码:
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 405, M = 800005;
int n;
int s[N]; //智商
int f[N]; //情商
int dp[M]; //dp[j]表示选取的奶牛智商和是j的情况下的最大情商和,j原本范围-400000~400000,避免负数+400000,最大值是800000
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> s[i] >> f[i];
}
memset(dp, -0x3f, sizeof(dp));
dp[400000] = 0;
for(int i = 1; i <= n; i++)
{
if(s[i] >= 0) //j-s[i]比j小,一维滚动要倒着遍历
{
for(int j = 800000; j - s[i] >= 0; j--)
{
dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
}
}
else //j-s[i]比较大,一维滚动要顺着遍历
{
for(int j = 0; j - s[i] <= 800000; j++)
{
dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
}
}
}
int ans = 0;
for(int j = 400000; j <= 800000; j++)
{
if(dp[j] >= 0) //情商不能为负数
{
ans = max(ans, dp[j] + j - 400000); //智商要减掉400000
}
}
cout << ans;
return 0;
}