OJ上恶心人的数据
好啦又到了OJ作业的时候。这次的OJ作业是DP,不同于上一篇DP,由于要上机操作,加上出题者会给你恶心的测试样例卡你,所以多了很多实际的问题。
题目:给定一个数组(long long型),最多可以删除一个数据,求最大的子数组和。
如:[1,2,-3,2] 输出5
[1,-2,-2,3]输出3
其实我感觉DP的难点就是思路,有了思路就很好构造DP数组。由于我之前做的DP题目比较少,我看到了这题陷入了一个惯性思维:构造一个二维数组,不停遍历,返回的dp[n][n]值则为最大值?
显然是错了,问题是二维数组怎么构造第二维啊?所以不是每一题DP都是像之前 学过的那样的。
这题的思路是:遍历三次一维DP,再遍历最后一次DP数组找最大值。
详细来说:第一次构造leftdp[n],代表的是以i结尾的连续子数组的最大值,第二次构造rightdp[n],代表的是以i为开头的连续数组的最大值,第三次构造dp[n],代表的是删除A[n]的情况下连续数组的最大值。最后还要进行一次遍历,找到到底删除哪个元素(或者不删)才最大呢?核心代码如下:
leftdp[0] = v[0];
rightdp[n - 1] = v[n - 1];
for (int i = 1; i < n; ++i) {
leftdp[i] = max(v[i], v[i] + leftdp[i - 1]);//以i为结尾:必须有v[i],看看要不要加以i-1为结尾的最大值
}
long long ans = rightdp[n - 1];
for (int i = n - 2; i >= 0; i--)
{
rightdp[i] = max(v[i], rightdp[i + 1] + v[i]);//以i为开头:必须有v[i],看看要不要加以i+1为开头的最大值
ans = max(ans, rightdp[i]); //这里遍历的是不删除情况下的最大值
}
for (int i = 1; i<n - 1; i++)
{
ans = max(ans, (leftdp[i - 1] + rightdp[i + 1]));//v[i]不取,左右开弓
}
cout << ans;
思路还是很妙的,还有一个点是就算是最后一次dp,每个数组存的也不是全局结果,而是条件结果,所以要找出全局最大值还要遍历所有条件结果。
但是!但是!我把代码提交过后有的测试样例提示我内存爆掉了!群里面的人说不能存数组!得边输入边处理!我当场裂开了。还好群里有一个善良的大佬提供了一份代码:
while (cin >> input) {
if (n == 0) {
d = 0; nd = input; ans = input;
}else {
d = (max((d + input), nd));
nd = (max(input, (input+nd)));
ans = max(ans, max(d, nd));
}
n++;
}
cout << ans;
思路也很相像,d是以当前输入数据为结尾的最大值(可以删除一个),nd就是不能删除下以当前输入数据为结尾的最大值。最大值存在于某一个数据为结尾的值里面,所以每一次输入数据都进行一次比大小。
离结果更近了一步,却发现OJ数据还藏着最后一个陷阱:下溢出
实际情况是OJ会给你两个负得贼鸡儿大的数据,它们两个加起来由于范围超过了 long long的下界所以下溢出了,结果变成了一个正数!
这种情况的解决方法是最开始都用long double(long long还大的数据格式)操作数据,最后返回的时候进行强制类型转换res=(long long)res,这样就不会下溢出了。不过为什么不能直接return long double呢?没太想清楚,有机会填坑吧。