[蓝桥杯][2019年第十届真题c/c++B组]后缀表达式(解释sum -= 2*a[i])

后缀表达式

给定 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。

解析

这道题目的坑是在于后缀表达式是可以加上括号计算的,而非直接贪心用最大的一些数减去最小的一些数。
括号和负号组合即可变号处理。

这道题的数据可以分为四种情况:

  1. 无减号(m=0):直接求所有数总和。
  2. 有减号(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;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你脸上有BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值