关于一道01背包问题的·拓展题的思考

[USACO03FALL]Cow Exhibition G

题目背景

题目描述

奶牛想证明它们是聪明而风趣的。为此,贝西筹备了一个奶牛博览会,她已经对 N N N 头奶牛进行了面试,确定了每头奶牛的智商和情商。

贝西有权选择让哪些奶牛参加展览。由于负的智商或情商会造成负面效果,所以贝西不希望出展奶牛的智商之和小于零,或情商之和小于零。满足这两个条件下,她希望出展奶牛的智商与情商之和越大越好,请帮助贝西求出这个最大值。

输入格式

第一行:单个整数 N N N 1 ≤ N ≤ 400 1 \le N \le 400 1N400

第二行到第 N + 1 N+1 N+1 行:第 i + 1 i+1 i+1 行有两个整数: S i S_i Si F i F_i Fi,表示第 i i i 头奶牛的智商和情商,− 1000 ≤ S i ; F i ≤ 1000 1000 \le S_i;F_i \le 1000 1000Si;Fi1000

输出格式

输出单个整数:表示情商与智商和的最大值。贝西可以不让任何奶牛参加展览,如果这样做是最好的,输出 0 0 0

样例 #1

样例输入 #1

5
-5 7
8 -6
6 -3
2 1
-8 -5

样例输出 #1

8

提示

选择第一头,第三头,第四头奶牛,智商和为−5+6+2 = 3,情商和为7−3+1 = 5。再加

入第二号奶牛可使总和提升到10,不过由于情商和变成负的了,所以是不允许的

这道题是01背包问题的一个拓展,挺难想到的,将智商当作体积,情商当作价值,
因为智商有负值,根据数据可算出智商之和: -400*1000 <= sum <= 400 * 1000,.
所以可以将智商加上偏移量400*1000,这样作为数组下标就不会越界了

画重点: 这题话要保证dp[j]是恰好智商和为j-M,如何保证呢?这就是下面代码中的先初始化所有值为-0x3f3f3f3f,这个数值的两倍不会溢出,然后初始化f[M]=0,这样在状态转移时我们最终取得max中的最大值是那些从0转移过去的状态,也就是恰好经过多次迭代dp[j] = max(dp[j], dp[j - s[i]] + f[i]);最终的最大值来源是从dp[M]也就是智商和为0转移过去的,这样就保证了我们最后的结果dp[j]一定是恰好智商和为j-M
思路参考了: 背包问题复习题单讲解
代码如下:

#include <algorithm>
#include <cstring>
#include <iostream>
#include <cstdio>

using namespace std;

const int N = 1e6 + 10, M = 400 * 1000;

int dp[N], n, s[N], f[N];

int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++)
        cin >> s[i] >> f[i];
    memset(dp, -0x3f, sizeof dp);//因为情商有负值,取max,要初始化负无穷
    dp[M] = 0;//这两行保证了dp[j]是恰好智商和为j-M
    for(int i = 0; i < n; i ++){
   		//对01背包进行了拓展,因为j - s[i]可能是大于j也可能是小于j
   		//一维化处理了,实际是dp[i][j],从前i个中在智商只有j-M时选的最大情商
   		//所以如果j - s[i]大于j就要正序遍历更新,因为用的是上一层后面的
   		//如果j - s[i]小于j就要逆序遍历更新,因为用的是上一层前面的
        if(s[i] >= 0)
            for(int j = 2 * M; j >= s[i]; j --)
                dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
        else
            for(int j = 0; j <= s[i] + 2 * M; j ++)
                dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
    }
    int  res = 0;
    for(int i = M; i <= 2 * M; i ++)
        if(dp[i] > 0)
            res = max(res, dp[i] + i - M);
    cout << res << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值