您的位置:首页 > 编程语言 > Java开发

JAVA实践非完美·在线处理算法求最大连续子序列和并输出首尾

2016-07-20 22:54 716 查看

前言

来自PAT:https://pta.patest.cn/pta/test/16/exam/4/question/663

Given a sequence of KK integers { N_1N
​1
​​ , N_2N
​2
​​ , ..., N_KN
​K
​​  }. A continuous subsequence is defined to be { N_iN
​i
​​ , N_{i+1}N
​i+1
​​ , ..., N_jN
​j
​​  } where 1 \le i \le j \le K1≤i≤j≤K. The Maximum Subsequence is the continuous subsequence which has the largest sum of its elements. For example, given sequence { -2, 11, -4, 13, -5, -2 }, its maximum subsequence is { 11, -4, 13 } with the largest sum being 20.

Now you are supposed to find the largest sum, together with the first and the last numbers of the maximum subsequence.

Input Specification:

Each input file contains one test case. Each case occupies two lines. The first line contains a positive integer KK (\le 10000≤10000). The second line contains KK numbers, separated by a space.

Output Specification:

For each test case, output in one line the largest sum, together with the first and the last numbers of the maximum subsequence. The numbers must be separated by one space, but there must be no extra space at the end of a line. In case that the maximum subsequence is not unique, output the one with the smallest indices ii and jj (as shown by the sample case). If all the KK numbers are negative, then its maximum sum is defined to be 0, and you are supposed to output the first and the last numbers of the whole sequence.

Sample Input:

10
-10 1 2 3 4 -5 -23 3 7 -21
Sample Output:

10 1 4


大致意思是求最大和,并且输出和,以及求和的那个序列的首和尾。

如果输入的全是负数,那么最大和应该是0,并且输出整个序列中的第一个和最后一个数字。

不知道我理解错了没。结果一个超时,一个错了。

其他节点通过。截图在结果下面。

实现功能

输入:

10 1 2 3 4 -5 -23 3 7 21

输出:

31 3 21

中文版参考

在线处理求最大和

/**
*
* 在线处理算法:
* “在线”的意思是指每输入一个数据就即时进行处理,在任何一个地方终止输入,算法都能正确给出当前的解。
*
* 非常巧妙的一个算法,线性时间复杂度
* 假设我们需要输入以下十个数字,求这个序列的最大和
* 【10 1 -20 3 4 -5 -23 3 7 21】
*
* 该算法将逐个数字读取,每读取一个处理一个
*
* 我们需要一个存储整体最大和的变量maxSum = 0
* 还需要一个存储临时序列的最大和的变量thisSum = 0
*
* 何为临时序列?像上面的序列中,逐个读取时,假设我们读取到了第2个,那前2个就算是临时序列,最大和是11
*
*
* 首先读取10,将其加起来,此时临时最大和为10      thisSum+=10 = 10
*     判断是否比整体最大和大,整体最大和是0,大,所以更新整体最大和为10 maxSum = thisSum = 10
*
* 再读取1,将其加起来,此时临时最大和为10 + 1 = 11   thisSum += 1 = 11
*     判断是否比整体最大和大,整体最大和是10,大,所以更新整体最大和为11 maxSum = thisSum = 11
*
* 再读取-20,将其加起来,此时临时最大和是-9      thisSum += -20 = -9
*      判断是否比整体最大和大,整体最大和是11,否
*      再判断是否已经小于0,是,那么临时最大和归零 thisSum = 0
*      ----------------------------------------------------------
*      这意味着我们将前3个当成临时序列了,求得前3个数字的最大和是11
*      thisSum归零表示我们重新计算下一个临时序列
*
*  再读取3,将其加起来,此时临时最大和是3      thisSum += 3 = 3
*      判断是否比整体最大和大,整体最大和是11,否
*      再判断是否已经小于0,否
*      那么不处理,继续读取下一个
*
*  再读取4,将其加起来,此时临时最大和是7      thisSum += 4 = 7
*      判断是否比整体最大和大,整体最大和是11,否
*      再判断是否已经小于0,否
*      那么不处理,继续读取下一个
*
*  重复以上步骤,经过n次循环后,maxSum的值就是序列最大和
*
* 再重复:
*  在线处理算法:
* “在线”的意思是指每输入一个数据就即时进行处理,在任何一个地方终止输入,算法都能正确给出当前的解。
*/


计算求和次数得首尾

【请先看代码】

【请先看代码】

【请先看代码】

使用在线处理算法求最大和时,要求该最大和序列的首尾对我来说不是一件容易的事。

想了略久,想出这么一个办法

预备说明:

临时最大和归零:执行过thisSum = 0

整体最大和:变量maxSum

/*
简单描述:
使用一个变量记录序列首元素head
另一个计数变量记录每次[从归零到下一次归零]的相加次数。computeCount

计数变量每循环一次就自增,在临时最大和清零时清零。

· 临时最大和是否进行过归零?
如果没有进行过归零
那么该序列的第一个数就是最大和序列的第一个数字。

如果进行过归零
在执行thisSum = 0,也就是临时最大和归零时,使用一个变量tHead临时存储[可能]的首元素。即tHead = i;
同时计数变量清零。重新计数
再分两种情况判断
1。归零后进行求和,求和过程中,临时最大和[大于]整体最大和
那么tHead就是我们需要的序列首元素,将其提取。
让head = tHead
2。归零后进行求和,求和过程中,临时最大和并[没有大于]整体最大和
那么tHead不是我们需要的序列首元素
经过处理后:
首尾元素就是arr[head+1]
尾元素就是arr[head + computeCount]
*/


代码实现

import java.util.Scanner;
public class OnlineDealMaxSubSequent {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] arr;
int[] p = new int[2];
int n;
while (in.hasNext()) {
n = in.nextInt();
arr = new int
;
for (int i = 0; i < n; i++) {
arr[i] = in.nextInt();
}
System.out.print(maxSubSum(arr, p) + " ");
System.out.println(p[0] + " " + p[1]);
}
}

public static int maxSubSum(int[] arr, int[] p) {
int thisSum = 0;
int maxSum = 0;

//计算相加次数
int computeCount = 0;
int tHead = -1;
int head;
boolean isRealHead = false;

for (int i = 0; i < arr.length; i++) {
thisSum += arr[i];
computeCount++;
if (thisSum > maxSum) {
maxSum = thisSum;
isRealHead = true;
} else if (thisSum < 0) {
computeCount=0;
thisSum = 0;
tHead = i;
}

if (isRealHead) {
//System.out.println("old: " + computeCount);
//如果tHead未经改变,那第一个就是起始数字
//如果tHead被改变过,且改变后序列和大于旧的序列和
head = tHead + 1;
isRealHead = false;
p[0] = arr[head];
p[1] = arr[head + computeCount - 1];
//System.out.println("head:" + arr[head]);
}
}

if (maxSum == 0) {
p[0] = arr[0];
p[1] = arr[arr.length - 1];
}
return maxSum;
}
}


结果

3
-2 -4 -3
0 -2 -3
10
10 1 2 3 4 -5 -23 3 7 21
31 3 21




收获嘛

啊,随便一道题就把我虐了,打完基础就好了。

智商不足以指望算过过活。

感觉这个算法真是巧妙到爆炸,俺的努力目标啊,写出这么优雅的代码。

自己花时间解决的事情,印象会很深刻

END

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息