感谢yzy神仙精辟入里的讲解.
题目描述
c++中可以宏定义max函数.c++中宏定义的max函数如下.
#define MAX(a,b) (a)>(b)?(a):(b)
但是它在调用的时候可能会出现重复计算加法的情况.
比如说调用MAX(2+3,4)的时候会变成(2+3)>(4)?(2+3):(4),那么这里就不止进行了一次的加法运算.
给出一个只含有数字,加号和MAX()函数的表达式,求出它的运算结果,并计算里面运行加法的次数.
样例: MAX(1+1+1,2)+MAX(2,3)
输出: 6 5
解释: 前面答案是3,后面也是3,加起来得6.
前面是(1+1+1)>(2)?(1+1+1):(2),很明显进行了4次加法,再加上后面的3一共进行了5次加法.
神仙题解
一开始我毫无头绪,只知道这是一道模拟题却不知从哪里下手.
结果yzy神仙写了一个递归秒了此题,将此题剖析得非常到位.这里来讲一下他的解法.
/*
递归[l,r]区间.如果区间中只有数字,直接返回此数字.
如果在区间中找到了一个不包含于任何一对括号之内的加号,则将此区间分为左右两部分递归求解.
结果就是两边的和,加法的次数是两边之和+1.
如果没有,则区间中只剩下了很多个MAX函数的嵌套.
首先找到最外层的MAX函数中的那个逗号,然后递归两边,按照宏定义的方式比较.
接下来要注意,较大的一边的加法计算要运行两次.
然后全部加起来,输出结果即可.
*/
#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
int x=0,f=1;char c=gc();
for (;!isdigit(c);c=gc()) f^=c=='-';
for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
return f?x:-x;
}
template <typename mitsuha>
inline bool read(mitsuha &x){
x=0;int f=1;char c=gc();
for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
if (!~c) return 0;
for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
return x=f?x:-x,1;
}
template <typename mitsuha>
inline int write(mitsuha x){
if (!x) return 0&pc(48);
if (x<0) x=-x,pc('-');
int bit[20],i,p=0;
for (;x;x/=10) bit[++p]=x%10;
for (i=p;i;--i) pc(bit[i]+48);
return 0;
}
inline char fuhao(){
char c=gc();
for (;isspace(c);c=gc());
return c;
}
}using namespace chtholly;
using namespace std;
const int yuzu=2e5;
char c[yuzu|10];
#define ans first
#define add second
typedef pair<int,int> node;
/*first存储运算结果,second存储加法运行次数.*/
int makenum(int l,int r){//l,r之间表示的那个数字.
int x=0,i;
for (i=l;i<=r;++i) x=x*10+c[i]-'0';
return x;
}
node work(int l,int r){//返回一个pair
int i,le;//le表示目前在第几层运算里.
for (i=l;i<=r;++i) if (!isdigit(c[i])) break;
if (i>r) return node(makenum(l,r),0);//全部都是数字,直接返回该数字.
for (i=l,le=0;i<=r;++i){//判断是否存在单独的加号.
if (c[i]=='(') le++;
if (c[i]==')') le--;
if (!le&&c[i]=='+') break;//le为0表示现在没有在任何一对括号里.
}
if (i<=r){//有单独加号
node a=work(l,i-1),b=work(i+1,r);//分别计算两边.
return node(a.ans+b.ans,a.add+b.add+1);//两边相加,注意加法的次数要+1.
}
for (i=l+4,le=0;i<=r;++i){//从l+4开始是因为要跳过"MAX("这一部分.
if (c[i]=='(') le++;
if (c[i]==')') le--;
if (!le&&c[i]==',') break;//寻找最外层的','
}
node a=work(l+4,i-1),b=work(i+1,r-1);//分别计算比较大小.
return a.ans>b.ans?node(a.ans,a.add*2+b.add):node(b.ans,b.add*2+a.add);
/*ans取较大的那一个,add取较大的那个两倍+小的那个.*/
}
int main(){
scanf("%s",c+1);
int n=strlen(c+1);
node llx=work(1,n);
printf("%d %d",llx.ans,llx.add);//一击必杀.
}
谢谢大家.