01背包问题,一开始看成是贪心了,两个数组算智商和情商,然后如果此次相加为0,就直接继承上一个状态,否则加上,后来发现根本不对,下一次的奶牛加上可能导致最大值变小,那如果是判断下一次奶牛的智商和情商是否能对总贡献有提升呢?也不对,假如只有两头奶牛,一个智商正一个情商正,其实这两个奶牛只要相加就都能满足题意,但是我们的策略判断第一头奶牛情商是负的直接跳过,第二头奶牛智商是负的直接跳过,那么会导致最后没有奶牛入选,得到错误答案。
其实正解是动态规划,贪心只能满足局部的最优解,但是这道题智商情商有正有负很明显这种局部最优解不能得到正确的答案,因为有时候先选择了某一方面有缺陷的奶牛在接下来的选择奶牛过程中也可能得到智商情商的双高,就比如说其中一头奶牛智商非常高,情商负数,其他奶牛智商都不高,但是有高情商,这样就会错失正确答案,而动态规划可以保证我们每个取值代表一个集合,而不单单是局部最优。
这道题有两方面限制,后来我又想到二维费用的背包问题,但是想不出来二维费用的边界,后来看题解才知道正解应该是转化为01背包求解。
将智商或情商其中一维表现为背包容量,另一维做物品价值,如果我们假设此时智商看作是背包容量,这时看起来只能判断出最后的价值也就是情商的最大值,我们如何取得智商和情商和的最大值呢?又是如何做到判断智商和情商都大于0的最大值答案呢?不用担心慢慢这些问题都会迎刃而解
递推式:我们考虑第i只奶牛选或不选,如果是不选择它那么就是继承上一个状态也就是f【i-1】【j-1】,如果选择就是f【i-1】【j-vi】+wi,我们要取这些答案的最大值,然后最后判断答案是否是小于0的,如果是则说明情商最后为负数不能要直接返回0,但是由于智商和情商是有负数的会影响递推式max取值,我们要考虑将f数组初始化为很小值,还有一个问题就是如果智商是负数可能导致数组越界因为智商是背包容量j,但是我们又不能特判掉它,如果特判掉它那就是和我们上面说的贪心法一样了,可能会漏掉正确答案,这时的做法是开一个大dp数组,然后用偏移量去计算它,我们一共可能有400头奶牛,每头奶牛最小情况是-1000,也就是40w,我们开80w,做前后的偏移,还有就是开一维数组,因为二维数组会爆空间。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 801100, M = 410;
int f[N], n;
struct node{int iq, eq;
}a[M];
int main()
{cin >> n;for (int i = 1; i <= n; ++ i ) cin >> a[i].iq >> a[i].eq;memset(f, -0x3f, sizeof f);f[400000] = 0;for (int i = 1; i <= n; ++ i ){if (a[i].iq >= 0){for (int j = 800000; j >= a[i].iq; -- j )f[j] = max(f[j], f[j - a[i].iq] + a[i].eq);}else{for (int j = 0; j <= 800000; ++ j )f[j] = max(f[j], f[j - a[i].iq] + a[i].eq);}}int ans = 0;for (int i = 400000; i <= 800000; ++ i )if (f[i] >= 0)ans = max(ans, f[i] + i - 400000);cout << ans << endl;return 0;
}
还有一些需要注意的点:比如说遇到正智商和遇到负智商的处理方式有所不同
这里的解释需要大家非常了解压缩版本的01背包,细想一下为什么二维的01背包(物品体积都为正数)可以容量从小往大遍历,但是压缩成一维后01背包只能容积从大到小遍历,这是因为我们每次更新ij用到的是上一层的状态,而压缩成一维后,如果容积还是从小到大,那么就会造成先更新本层小容积背包,再去更新大容积背包时候使用的我们本层的小容积背包,而并不是上一层的,那如果先更新大容积呢?这时本层小容积还没来得及更新,所以使用的还是上一层的,这也就是为什么压缩成01了要调转遍历容积顺序,而如果体积变成负数了,也是同样的由于我们要使用上一层的状态,更新小容积时由于体积为负,所以要使用大体积,这时大体积没更新是上一层的,我们更新大体积时候使用的是更大体积,所以要正向遍历。
还有就是智力为负数所以j-vi一定为正数我们要从0开始走,不然会出现漏掉一些智力的情况
最后的取值判断fi大于等于0是为了判断情商不能是负数,而智商从400000也就是头部开始走,相当于从1或者0开始走,固然智力也不为负,然后取最大值,不要忘记减掉偏移量,fi+i就是智商加情商的最大值。
总体来看这道题需要注意的细节非常之多,稍不注意就会re或者wa,是非常好的一道练习01背包的题目。