11.21

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呢?没太想清楚,有机会填坑吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值