后缀表达式
给定 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。
解析
这道题目的坑是在于后缀表达式是可以加上括号计算的,而非直接贪心用最大的一些数减去最小的一些数。
括号和负号组合即可变号处理。
这道题的数据可以分为四种情况:
- 无减号(m=0):直接求所有数总和。
- 有减号(m!= 0):
A.无负数:总和-最小正数
B.负数个数等于总的元素个数:若负数个数等于n+m+1的话,肯定会留出一个负数来,无法通过加括号的形式变为正的,那么就只能加上这个负数。
贪心的思想可知,若要使最后的和最大,那么这个负数也要最大(绝对值最小)。
C.负数个数不等于总的元素个数:可以通过加括号的形式,将所有的减号变为加号的。故和即为所有元素的绝对值之和。
解释:-2*a[i]
看一下代码中的sum -= 2ll*a[i];
为什么是减两倍呢?
这是因为sum += a[i];
我们在边输入的同时已经用sum累加了所有元素的和。
在没有减号的情况下,就可以直接输出结果sum了。
但是在有减号的情况下,就要思考一下了,例如现有数组:
a[i] = {-2,-1,2,3,5};
我们的sum此时的结果是所有数的和,sum=(-2)+(-1)+2+3+5;负数在sum中扮演的是减去的角色。
而在我们上面列举的几种情况中:
如C情况,我们需要求的是所有元素的绝对值之和,也就是说应该变为sum=1+2+2+3+5。
这样就应该理解了,我们应该减去两倍的a[i](a[i]此时是负数,减去负数即加上正数,负负得正)
i循环的次数=负数的个数,在输入数据的时候就可以用一个变量来计录负数的个数。
需要注意的是B情况,因为负数个数和所有元素个数相同,故会有一个负数不变号不能加回,故循环的次数=负数个数-1。
而这个负数应为最大的负数(绝对值最小),也就是i=fu-1的元素值,如a[i] = {-2,-1,2,3,5};
这个负数就是-1.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
ll a[maxn];
int main() {
int N, M;
cin >> N >>M; //N个加号,M个减号
int num = N+M+1;
int fu = 0; //统计负数的个数
ll sum = 0;
for (int i = 0; i < num; i++) {
cin >> a[i];
sum += a[i];
if(a[i] < 0) fu++;
}
sort(a, a+num);
if (M) { //如果有减法,需要对比总数目与负数数目
if (fu) { //如果有负数
if (fu == num) { //负数的数目与总目相等(必有一个负数无法通过加括号的形式变为正,故不能加回)
for(int i = 0; i < fu - 1; i++){
sum -= 2ll*a[i]; //输入时sum减过一次这些负数,现在是加回负元素,故应减去两倍(负负得正)。
}
} else { //负数的数目与总数目不相等(所有负数都可以变正,故将所有负数加回)
for (int i = 0; i < fu; i++) {
sum -= 2ll*a[i];
}
}
} else { //如果没有负数,就只有一个负号起作用,减去正数min
sum -= 2ll * a[0];
}
}
//如果没有减法,则最大值就直接是所有数的和
cout << sum << endl;
}