第十届蓝桥杯C/C++_B组 题目汇总
试题 I: 后缀表达式
时间限制: 1.0s 内存限制: 256.0MB 本题总分:25 分
【问题描述】
给定 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,−109 ≤ Ai ≤109
解题思路
这题很容易想成贪心,即后缀表达式也是表达式,又没有括号,那就尽量加值大的数,减值小的。然而并不是~
后缀表达式实际上是隐藏括号的。 我们可也借助这一特点来变减号为加号。 比如:两个减号,三个数字3 2 1,要是按照前面说的贪心思路来,答案应该是3 - 2 - 1 = 0,即 3 2 - 1 - ,但是如果我们写成, 3 1 2 - -,即3 - (1 - 2) = 3-1+2 = 4,秀不秀??6不6? 明白了这点,我们可以得出结论,假如我们现在有m个减号,那么我们可以把这m个减号变成 k (1<k<=m)个减号,和m-k个加号。
现在有n个加号和m个减号,下面分情况讨论:
① 负数个数>=m ,因为减去一个负数相当于加上他的绝对值,那么为了得到表达式最大值,我们要减去前m小的负数,剩下的所有数用加法。
//3.31 修改
② 负数个数 ne ∈ [0 , m) ,为了使表达式的值最大,我们应该把这m个减号转化为ne个减号和m-ne个加号,即减去所有负数,加上所有正数。注意最少要保留一个减号,即若没有负数且存在减号,那么要减去一个最小正数。
代码
代码不对,我懒得改,
// 3.31 修改
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+5;
ll pnum[maxn],nnum[maxn]; // 存正数和负数
bool cmp(ll x,ll y)
{
return abs(x) > abs(y);
}
int main()
{
int n,m;
int p=0,ne=0;
ll t;
scanf("%d %d",&n,&m);
for(int i=1;i<=n+m+1;i++) {
scanf("%lld",&t);
if(t >= 0)
pnum[++p] = t;
else
nnum[++ne] = t;
}
sort(pnum+1,pnum+p+1,cmp);
sort(nnum+1,nnum+ne+1,cmp);
ll ans=0;
// 当负数个数 >= 减号个数
if(ne >= m) {
for(int i=1;i<=m;i++) // 减去abs前m大的负数
ans -= nnum[i];
for(int i=m+1;i<=ne;i++) //加上剩下的负数
ans += nnum[i];
for(int i=1;i<=p;i++) // 加上所有正数
ans += pnum[i];
}
else { // 负数个数 < 减号个数
for(int i=1;i<=ne;i++) // 减去所有负数
ans -= nnum[i];
for(int i=1;i<=p;i++) //将多余的减号全部转化为加号,
ans += pnum[i];
if(ne == 0)
ans -= pnum[p]*2; //最小的正数加上过一次了,实际上应该减去,所以这里要减去两次
}
printf("%lld",ans);
}