蓝桥杯 历届真题 题解目录
试题 I: 后缀表达式
时间限制: 1.0s 内存限制: 256.0MB 本题总分:25 分
【问题描述】
给定 N 个加号、M 个减号以及 N + M + 1 个整数 A1,A2,··· , A N + M + 1 A_{N+M+1} AN+M+1,小明想知道在所有由这 N 个加号、M 个减号以及 N + M +1 个整数凑出的合法的 后缀表达式中,结果最大的是哪一个?
请你输出这个最大的结果。
例如使用1 2 3 + -,则 “2 3 + 1 -” 这个后缀表达式结果是 4,是最大的。
【输入格式】
第一行包含两个整数 N 和 M。 第二行包含 N + M + 1 个整数 A1,A2,··· , A N + M + 1 A_{N+M+1} AN+M+1。
【输出格式】
输出一个整数,代表答案。
【样例输入】
1 1
1 2 3
【样例输出】
4
【评测用例规模与约定】
对于所有评测用例, 0 ≤ N , M ≤ 100000 , − 1 0 9 ≤ A i ≤ 1 0 9 0≤ N,M ≤100000,-10^9 ≤ Ai ≤10^9 0≤N,M≤100000,−109≤Ai≤109。
分析:
本来我一看这题下意识以为是动态规划的,就像蓝桥杯题库的那道最大的算式一样。
但是吧,最大的算式中要求N个数的相对位置不能改变,就没法贪心了。而这个题就不同了,他说只要是合法的后缀表达式就行。况且最大的算式N最大才15,这道题n+m+1可是200001!dp[200001][200001]的数组全局变量都放不下。。。
言归正传。很多人都认为,后缀表达式是没有括号的,所以不用考虑a-(b+c)的情况出现,但是却不然。
a
b
c
+
−
a b c + -
abc+−这个后缀表达式是怎么算的?就是a-(b+c)。所以后缀表达式是要考虑括号的。
首先,如果有括号,会造成什么效果?当然就是传说中的负负得正了~
比如:1-((-2) + (-3) + (-4)) - (-5) - (-6) + 7 + 8 + 9 + 10 = 55;大家发现了什么?显然结果就是序列绝对值的和。
但我们要考虑几种特别情况:第一,如果我没有减号,只有加号,显然括号不会对其运算结果有任何影响,直接相加即可。
第二,如果我们有减号,却没有负数,总有一项没法负负得正,就只好牺牲最小的正数了。例如:
1 3
1 2 3 4 5
就只好5 + 4 - (1 - 2 - 3) = 5 + 4 + 3 + 2 - 1 = 14了。
同样,如果没有正数,那么表达式中的第一个数无论怎样都无法负负得正的,这时就要牺牲最大的负数,即绝对值最小的负数。例如:
0 2
-1 -2 -3
就只好-1 - (-2) - (-3)= 3 + 2 - 1 = 5了。
否则,但凡有一个减号,有负数也能变成绝对值相加。
2020-3-31更新:
最开始我没有考虑数据范围,这道题结果的最大值是200001*1e9,是超过int范围的,但并不超过long long,所以获取结果的变量ans应该定义成long long。这里感谢博友的提醒φ(* ̄0 ̄),如果还有什么问题,也希望大家一起讨论,谢谢啦§( ̄▽ ̄)§
2020-9-18更新:
初值不记得当初为什么设的1和-1了,这里主要是判断是否有负数或者是否有正数用的,谢谢博友提醒,现在改过来了,变为下面的整形极限值。
这1和-1连我自己的样例都跑不过,我也是醉了,什么时候变成1和-1的……[・_・?]
/* int MIN = 1, MAX = -1; */
int MIN = INT_MAX, MAX = INT_MIN;
代码如下:
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int n, m, len;
int *a;
long long ans = 0;
int MIN = INT_MAX, MAX = INT_MIN;
int main()
{
freopen("text.ini", "r", stdin);
cin >> n >> m; //n个+,m个-
len = n + m + 1;
a = new int[len];
for (int i = 0; i < len; i++)
{
cin >> a[i];
MIN = min(MIN, a[i]);
MAX = max(MAX, a[i]);
}
if (!m)
{ //如果没有减号
for (int i = 0; i < len; i++)
ans += a[i];
}
else
{ //如果有减号
for (int i = 0; i < len; i++)
ans += abs(a[i]);
//如果没有负数,结果应减去最小值。
if (MIN > 0) ans -= MIN * 2;
//如果没有正数,结果应加上最大值
if (MAX < 0) ans += MAX * 2;
}
cout << ans << endl;
cout << "over" << endl;
while (1);
return 0;
}
附带个人写的几个样例:
样例输入1:
1 1
2 -1 -1
样例输出1:
4
样例1解释:
2 - ((-1) + (-1)) = 4
样例输入2:
1 1
1 2 3
样例输出2:
4
样例2解释:
3 + 2 - 1 = 4
样例输入3:
2 2
1 -2 3 -4 5
样例输出3:
15
样例3解释:
5 + 3 + 1 - (-2) - (-4) = 15
样例输入4:
5 5
1 -2 -3 -4 5 -6 -7 -8 -9 -10 -11
样例输出4:
66
样例4解释:
1 + 5 - ((-2) + (-3) + (-4) + (-6) + (-7)) - (-8) - (-9) - (-10) - (-11) = 66