【蓝桥杯】后缀表达式(Java实现,详细的分析)

题目信息

【题目描述】
给定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++相比太慢了。

希望大家指出我的不足,一起进步!
第一次写博客!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值