欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

和最大子序列 蓝桥杯VIP试题 Java题解(这题坑多)

程序员文章站 2022-06-13 10:22:09
...

题目描述:

试题 算法提高 和最大子序列

资源限制
时间限制:1.0s 内存限制:512.0MB
问题描述
  对于一个给定的长度为N的整数序列A,它的“子序列”的定义是:A中非空的一段连续的元素(整数)。你要完成的任务是,在所有可能的子序列中,找到一个子序列,该子序列中所有元素的和是最大的(跟其他所有子序列相比)。程序要求你输出这个最大值。
输入格式
  输入文件的第一行包含一个整数N,第二行包含N个整数,表示A。
  其中
  1 <= N <= 100000
  -10000 <= A[i] <=  10000
输出格式
  输出仅包含一个整数,表示你算出的答案。
样例输入
5
3 -2 3 -5 4

样例输出
4

思路: 求最大子序列问题,题设中说的是:“A中非空的一段连续的元素(整数)”

  • 暴力解法,列出所有子序列,计算出结果,再做比较(时间复杂度太高,不推荐,本题测试数据是10万个,会timeout)
  • 从头开始,使用一个临时变量来存储当前子序列的和,如果值小于等于0,就舍弃当前序列,重新开始,再使用一个变量来存储历史出现的最大值,最终输出这个变量就可以了
import java.util.Scanner;

/**
 * 子数组连续累加和
 */
public class Main {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int[] arr = new int[n];
		//接受数据
		for(int i=0;i<n;i++){
			arr[i] = in.nextInt();
		}
		//处理数据
		int max = arr[0];
		//临时累加和
		int tempMax = 0;
		int q = 0;
		//对数组进行处理
		while(q<arr.length){
			//谁大就变成谁
			max = Math.max(tempMax,max);
			tempMax += arr[q++];
			//当当前累加和不能对后面累加做贡献时,舍弃
			if(tempMax<=0)
				tempMax = 0;
		}
		System.out.println(max);
	}
}

能成功进行处理
和最大子序列 蓝桥杯VIP试题 Java题解(这题坑多)
但是如果你去提交代码,肯定会这样:
和最大子序列 蓝桥杯VIP试题 Java题解(这题坑多)
嗯?我们不妨把测试数据打开看看,再放到本地跑一下:
和最大子序列 蓝桥杯VIP试题 Java题解(这题坑多)
这数据,有点过分了,但是你以为这就是全部吗?
来看看本地运行结果:
和最大子序列 蓝桥杯VIP试题 Java题解(这题坑多)
具体原因是什么呢?我当时也很蒙蔽,又返回去看了一下题目条件,发现没有问题,值得范围是-10000~10000,没有超过int类型范围,然后我就去度娘上面查了一下这个InputMismatchException异常,发现是输入类型不匹配造成的异常,然后我把nextInt()换成next()试试:

	arr[i] = Integer.parseInt(in.next());

然后就发现了一些奇怪的东西:
和最大子序列 蓝桥杯VIP试题 Java题解(这题坑多)
原来是把两个数据合在放在一起了,网上一些大佬说c++里面可以读取这种数据,我试了试c++,也不能,代码:希望大佬纠正一下:

#include <iostream> 
#include <algorithm>

using namespace std;

int main()
{
	int n = 0;
	cin>>n;
	int *arr = new int[n];
	int i = 0;
	for(i=0;i<n;i++){
		cin>>arr[i];
	}
	//处理数据
	int maxVal = arr[0];
	//临时累加和
	int tempMax = 0;
	int q = 0;
	//对数组进行处理
	while(q<n){
		//谁大就变成谁
		maxVal = max(tempMax,maxVal);
		tempMax += arr[q++];
		//当当前累加和不能对后面累加做贡献时,舍弃
		if(tempMax<=0)
			tempMax = 0;
	}
	cout<<maxVal<<endl; 
	return 0;
}

言归正传,回到Java中,我们可以这样过滤一下数据:

	String next = in.next();
	//负号不在第一位
	if(next.lastIndexOf("-")>0){
		//分割一下
		String str1 = next.substring(0, next.lastIndexOf("-"));
		arr[i++] = Integer.parseInt(str1);
		String str2 = next.substring(next.lastIndexOf("-"));
		arr[i] = Integer.parseInt(str2);
		continue;
	}
	arr[i] = Integer.parseInt(next);

测试一下:
和最大子序列 蓝桥杯VIP试题 Java题解(这题坑多)
这次没错了,但是程序一直处于阻塞状态,盲猜两个正数放在一个了,导致输入的数没有10万个,in.next()导致程序阻塞,验证一下:

	if(next.length()>6){
		System.err.println(next);
		return;
	}

因为数据范围是-10000~10000所以最高只能有长度5,但是拼接过后就会超过这个长度
和最大子序列 蓝桥杯VIP试题 Java题解(这题坑多)
果然,有这些捣乱的数,因为int返回最大值数是2147483647,所以不会超限,这里我观察了一下运行的正确答案是33227197,正好就是那个最大的数,所以是不能再进行分割了(如果是为了AC),但是这道题最主要的是掌握求子序列最大和的方法,过于纠结这个测试数据就有点本末倒置了,所以,后面我们直接更改一下接收条件:

for(int i=0;i<n-1;i++)

和最大子序列 蓝桥杯VIP试题 Java题解(这题坑多)

博客后半段只是为了发现问题,但不推荐这样做,重要的是理解思想,同时此方法还可以边接收边处理。

如果错误请指正!