洛谷题单指南-动态规划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;
}

 

posted @ 2024-05-08 15:10  五月江城  阅读(20)  评论(0编辑  收藏  举报