题目信息
【题目描述】
给定N 个加号、M 个减号以及N + M + 1 个整数A1,A2,…,AN+M+1
小明想知道在所有由这N 个加号、M 个减号以及N + M +1 个整数凑出的合法的后缀表达式中,结果最大的是哪一个?
请你输出这个最大的结果。
例如使用1 2 3 + -,则“2 3 + 1 -” 这个后缀表达式结果是4,是最大的。
【输入格式】
第一行包含两个整数N 和M。
第二行包含N + M + 1 个整数A1,A2,…,AN+M+1
【输出格式】
输出一个整数,代表答案。
【样例输入】
1 1
1 2 3
【样例输出】
4
【评测用例规模与约定】
0<=N,M<=100000,-10^9<= Ai <=10^9
解题注意
这道题最大的难点在于对后缀表达式的理解
【思维误区】
第一眼看到这道题很容易将后缀表达式当成普通表达式简化处理,然后就想当然的排序,优先加大数减小数,错的浑然不知。
注意:当然根据这题25分的分值也可以想到没有那么简单。
后缀表达式示例图
对应后缀表达式:3 1 2 - -
等价普通表达式:3-(1-2)=4
可以看到后缀表达式可以理解为加括号,有的-号可以转化成+号
正确思路
如果你之前对此类问题没有过研究的话,可以先大体将问题分情况讨论,题目给了m,n为+,-号数量,可以先根据此划分。
1.当n=0时,即全为+号时。答案为所有数之和,毫无疑问。
2.当m=0时,即全为-号时,因为多个-号可以加()变为+号,如:3-(1-2)=4
但是因为正负数不知道,何时变-号为+号需要讨论。
为便于理解:设所有数为a1~ax,最大数为max,最小为min,所有数之和为nums。
(1)当全为正数时:|max|最大,|min|最小
max-(min-a1-a2-…-ax)=nums-2min,显然是最大的情况(你不可能加上所有正数,最少要减去一个数)。
(2)当全为负数时:|max|最小,|min|最大
max-min-a1-a2-…-ax=-nums+2max,显然是最大的情况(你不可能减去所有负数,最少有一个负数是加)
(3)当正负数都存在时:a正为数组a中的正数,a负为数组a中的负数。
I.当只有两个数时,a正-a负=a正+|a负|显然最大。
II.当a正数量大于1时,a正-(a负-a正-…-a正),即将正数向括号里面放,结果为所有数绝对值之和,显然最大。
III.当a负数量大于1时,a正-a负-a负-…
上述II、III可以互补考虑,即增加数的数量时,是放入括号里面变+还是放在外面-,总之就是+绝对值。
3.当m!=0&m!=0时,即+、-号都存在时。一时想不出来可以按上面一样分情况。
(1)当全为正数时:|max|最大,|min|最小
max+a3+a4+…-(min-a1-a2-…-ax)=nums-2min,显然是最大的情况(你不可能加上所有正数,最少要减去一个数)。
(2)当全为负数时:|max|最小,|min|最大
max-(min+a1+a2+…)-a3-a4-…ax=-nums+2max,显然是最大的情况(你不可能减去所有负数,最少要加上一个负数)。
(3)当正负数都存在时:a正为数组a中的正数,a负为数组a中的负数。
I.当只有两个数时,a正-a负=a正+|a负|显然最大。(可能你会说至少3个数,这步只是辅助思考,请继续向下看)。
II.当多个数时,每次增加一个符号一个数字。
当+和正数时,直接+a正,结果为所有数绝对值之和,显然最大。
当+和负数时,a正-(a负+a负),结果为所有数绝对值之和,显然最大。
当-和正数时,a正-(a负-a正),结果为所有数绝对值之和,显然最大。
当-和负数时,直接-a负,结果为所有数绝对值之和,显然最大。
可能你会说我忘记考虑全为0了,代码中先初始化了结果num为0.
总的来说思路就是先从2个数字分析,每次放入一个符号和一个数字,使+正或-负使得结果保持最大。
代码区
首先根据题目用例规范,最多200000个10^9相加,显然超过int,类型,但小于long,不必用Integer。
然后时间方面要求,解法中只有存在累加和取绝对值,最多加开始的输入,应该为O(n),满足题目需求。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
// 后缀表达式与前缀表达式在计算过程中是可以存在括号“()”的
// 1.当-号个数为0时,所有数之和
// 2.当+号个数为0时, 全为正数,max-(min-a-b-...)所有数之和-2*最小的数
// 全为负数,max-min-a-b-...所有数之和的相反数-2*|max|
// 上述两种情况合并,所有数绝对值之和-2*MIN|x|
// 有正数有负数,正数-(负数-x个正数)-y个负数,即所有数绝对值之和
// 3.当+、-号都存在时,全为正数,max-(min-a-b-...)+c+d+...,即所有数之和-2*min
// 全为负数,max-(min+a+b+...)-c-d-...,即所有数之和的相反数-2*|max|
// 上述两种情况合并,所有数绝对值之和-2*MIN|x|
// 有正有负,x个正数+正数-(y个负数-x个正数)-y个负数,即所有数绝对值之和
public static void main(String[] args) {
int m,n;
Scanner scanner=new Scanner(System.in);
m=scanner.nextInt();
n=scanner.nextInt();
boolean x=false,y=false;//判断正负数是否存在
int length=m+n+1;
int[] a=new int[length];
a[0]=scanner.nextInt();//第一个数
int max=a[0];
int min=a[0];
if(a[0]>0) x=true;
else if(a[0]<0) y=true;
for(int i=1;i<length;i++) {
a[i]=scanner.nextInt();
if(min>a[i]) min=a[i];
if(max<a[i]) max=a[i];
if(a[i]>0) x=true;
else if(a[i]<0) y=true;
}
long num=0;
if(n==0) {//-号个数为0,所有数之和
for(int i=0;i<length;i++)
num+=a[i];
}
else if(m==0) {//+号个数为0
if(x&&y) {//正负数都存在
for(int i=0;i<length;i++)
num+=Math.abs(a[i]);
}
else if(x&&!y) {//全正数
for(int i=0;i<length;i++)
num+=a[i];
num=num-2*min;
}
else if(!x&&y) {//全负数
for(int i=0;i<length;i++)
num-=a[i];
num=num+2*max;
}
}
else {//有+、-号
if(x&&y) {//正负数都存在
for(int i=0;i<length;i++)
num+=Math.abs(a[i]);
}
else if(x&&!y) {//全正数
for(int i=0;i<length;i++)
num+=a[i];
num=num-2*min;
}
else if(!x&&y) {//全负数
for(int i=0;i<length;i++)
num-=a[i];
num=num+2*max;
}
}
System.out.println(num);
}
}
测试网址:link.
欢迎交流
可能你觉得我可以合并以上讨论的情况,但我觉得这样写代码虽然冗长一点,但容易理解和复习,也不怎么影响时间复杂度。
另外,想问一下怎么提高Java得运行速度,与c++相比太慢了。
希望大家指出我的不足,一起进步!
第一次写博客!