https://vjudge.net/problem/HD...ios
一句话题意:对一个含有
MAX()
宏与'+'
与','
的表达式进行计算,并求出要计算多少次加法;其中#define MAX(a,b) ((a)>(b)?(a):(b))
。c++
宏是基于“替换”工做的。segmentfault
这也就是所谓的“正则序”(参考SICP第一章),不断地将宏名展开,反复进行替换过程,直到无可替换。ide
也就是说,MAX(1+2,3)
会被替换为(1+2)>(3)?(1+2):(3)
。而计算替换以后的表达式的值则需作2
次加法。比起传入函数max(int a,int b){return a>b?a:b;}
内,多计算了一次加法。函数
解:spa
咱们能够对整个表达式字符串左往右扫一次,对遇到的是啥字符分类讨论。这也是我上一篇文章提到的“局部分离”的一种体现——先不看总体,先着眼局部——局部正确了,总体天然正确
。这也就是所谓的分而治之(Divide & Conquer)
。这种详细列出每种情形的思想,也差很少能够算做有限状态机
和所谓状态转移
的思想。.net
这个题目,用递归作。我是这么想的:code
1.先实现一个最简单的加法/纯数字表达式解析函数,叫read
好了(其实应该叫parse
比较好)。递归
这个read
函数要解析不含括号,只含+
的表达式。那么从左到右遍历一次吧!索引
遇到加号
:把临时寄存器
里的数字加进结果寄存器
里,而且加法计数器
加1;
遇到数字
就根据十进制数字转换法加进临时寄存器
里(方法是,临时寄存器
= 临时寄存器
*10+遇到的这个数字
);
边界条件:读完表达式时,临时寄存器
里的数字再加进结果寄存器
里。(也能够在表达式字符串读进来以后,末尾加上一个+
,再让加法计数器
减1,这算不算够优雅呢?)。
仔细体会:这个最简单的加法表达式解析程序只有三个状态:1.遇到加号;2.遇到数字;3.边界,即字符串读完。
2.再扩展上一步的read
函数,使其支持含前、后括号
与含逗号
的表达式。
题意有以下约定:前括号总和MAX
一块儿出现,如MAX(
。所以,能够忽略MAX这些字符,用前括号表明MAX
宏。
仍然是从左向右扫一遍。索引(index)记为i
;但不用for的自增i
(如for(;;i++)
),咱们手动根据不一样的状态增长。
新增状态* 遇到前括号
,根据题意,输入的表达式必定合法。因此目前遇到前括号
,则之后必定会遇到后括号
,那么如今直接递归提取出这对括号内的逗号两边的值,算一发吧!把下一个字符的i
传给函数,i
置为函数返回值,递归读出逗号前边的表达式,记为op1
(operand1)。而后,把下一个字符的i
传给函数,i
置为函数返回值
,递归读出逗号前边的表达式,记为op2
(operand2);根据MAX的法则,计算MAX(op1,op2)
的结果。return read(i+1)
。作完这个步骤以后,这对括号内的表达式就解析完毕了。
新增状态* 遇到逗号
,结果寄存器+=临时寄存器
,临时寄存器 = 0
,return i
;
新增状态* 遇到后括号
,同上一条。
新增状态* 遇到字母
, i++
,即无视这个字符,去看下一个字符咯。
修改状态* 遇到加号
, 在第一步的基础上,return read(i+1)
。
文字描述不免难懂。请看代码。
3.收工
真的,实现了上面两个功能,这题就作完了。实现的细节用到了c++的引用机制
。就是你看到的&
。递归 + 引用,描述了解析实现的本质,美滋滋。
注释1:bl
这个结构体是啥的简写呢?……我忽然忘了,应该是blanket
的简写。彻底意义不明…
注释2:bl
内的value
表明表达式的值,而cnt
是count
的简写,表明这个表达式内部进行了多少次加法
。
//AC,0ms #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<sstream> #include<algorithm> #include<iostream> #include<stack> #include<vector> #include<set> #include<map> #define LL long long using namespace std; struct bl{ int value,cnt; bl(int v=0,int c=0):value(v),cnt(c){}; }; string s; int read(int index,bl &p){ int i,num=0; for(i = index;i<s.length();){ if(s[i] == '('){ bl op1,op2; i = read(i+1,op1); i = read(i+1,op2); if(op1.value > op2.value){ p.cnt += 2*op1.cnt + op2.cnt; p.value += op1.value; }else{ p.cnt += 2*op2.cnt + op1.cnt; p.value += op2.value; } return read(i+1,p); }else if(s[i] == ')'){ p.value += num; num = 0; return i; }else if(s[i] == ','){ p.value += num; num = 0; return i; }else if(s[i] == '+'){ p.value += num; p.cnt ++; num = 0; return read(i+1,p); }else if(s[i] >='0' && s[i] <='9'){ num = num*10 + s[i] - '0'; i++; }else{ i++; } } if(num) p.value+=num; return i; } int main(){ int t; cin >> t; while(t--){ cin >> s; bl re; read(0,re); cout << re.value << " " << re.cnt << endl; } return 0; }
嗨呀,递归真是漂亮啊!
recursion a day,keep the stack away!
括号表达式解析就是要递归才对吧!!!
……不过话说回来,我想出这个递归解法用了很久时间。我也是半路用上状态思想的。
可是只要想清楚每种状态
及其转移
,实现就如探囊取物了。