UVA198 Peter's Calculator 题解
PART 0:广告
无耻推荐个人博客
话说沙漠寻宝都那么多人做,这一题怎么没人愿意碰?
PART 1:题目大意
题目要求我们手写写编译器,所以建议评黑
好吧,其实我们只需要读入一些式子,对于每个PRINT 语句输出后面表达式的值,如果变量没有被定义,则输出UNDEF。(那其实还是编译器)
PART 2:解题思路
这一道题,无非是一个大模拟题,只要按照题目的意思写就好了。
这道题目类似与P2229 [HNOI2002]沙漠寻宝]类似,但是比沙漠寻宝更恶心的地方是,题目中的表达式可以先用在定义,只要在输出之前定义了就不算为定义
如果不能理解的话,可以看样例第一行,第一行定义了 a = b + c ,但此时b和c还没有被定义,但没有关系接下来的两行中定义了它们的值。所以之后输出的时候仍然可以计算。
对于这种情况,我们可以连着其他变量一起把当前表达式记录下来,当遇到的是数字,我们正常计算;若遇到变量,我们就再次调用计算的函数区计算当前变量的值,用计算的出的值参与运算。对于计算,可以采用中缀表达式转后缀表达式计算的方法来计算,我这里不在赘述。于是可以得到这样的代码:
/*
data -> 变量对应的表达式
piror -> 运算符优先级,其中:
piror['*'] = piror['/'] = 2;
piror['+'] = piror['-'] = 1;
piror['('] = 0;
piror[')'] = 3;
isdefined -> 变量是否被定义
*/
bool flag;
map<char,int> piror;
map<string,string> data;
map<string,bool> isdefined;
bool isletter(char c){ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
bool issymbol(char c){ return (c == '+' || c == '-' || c == '*' || c == '(' || c == ')'); }
int calculator(string expre){
if(!flag) return 0;
stack<int> num;
stack<char> symbol;
for(int i = 0;i < expre.length();i++){
if(isdigit(expre[i])){
int x = 0;
while(i < expre.length() && isdigit(expre[i])){
x = x*10 + expre[i] - '0';
i++;
}
i--;
num.push(x);
}else if(isletter(expre[i])){
string tmp;
while(i < expre.length() && ( isdigit(expre[i]) || isletter(expre[i]) ) ){
tmp += expre[i];
i++;
}
i--;
if(!isdefined[tmp]){
flag = false;
return 0;
}
num.push(calculator(data[tmp]));
}else if(issymbol(expre[i])){
if(symbol.empty()) symbol.push(expre[i]);
else if(expre[i] == '(') symbol.push(expre[i]);
else if(expre[i] == ')'){
while(!symbol.empty() && symbol.top() != '('){
char c = symbol.top(); symbol.pop();
int b = num.top(); num.pop();
int a = num.top(); num.pop();
if(c == '+') num.push(a + b);
else if(c == '-') num.push(a - b);
else if(c == '*') num.push(a * b);
}
if(!symbol.empty()) symbol.pop();
}else{
while(!symbol.empty() && piror[symbol.top()] >= piror[expre[i]]){
char c = symbol.top(); symbol.pop();
int b = num.top(); num.pop();
int a = num.top(); num.pop();
if(c == '+') num.push(a + b);
else if(c == '-') num.push(a - b);
else if(c == '*') num.push(a * b);
}
symbol.push(expre[i]);
}
}
}
while(!symbol.empty()){
char c = symbol.top(); symbol.pop();
int b = num.top(); num.pop();
int a = num.top(); num.pop();
if(c == '+') num.push(a + b);
else if(c == '-') num.push(a - b);
else if(c == '*') num.push(a * b);
}
return num.top();
}
看上去很正确,实在是完美无缺,对不对?
那我下面这一组数据上面的程序会怎么处理呢:
a := b + c
b := a + c
PRINT a
手推一下就会知道,如果是上面这样的程序,显然会无限递归下去,你的程序就愉快的 RE 了。这种情况应该是输出 UNDEF 的。我们可以用一个 \(vis\) 数组记录变量是否递归计算过了,如果重复递归计算就直接返回,并输出 UNDEF :
/*
vis -> 变量是否被计算过
data -> 变量对应的表达式
piror -> 运算符优先级,其中:
piror['*'] = piror['/'] = 2;
piror['+'] = piror['-'] = 1;
piror['('] = 0;
piror[')'] = 3;
isdefined -> 变量是否被定义
*/
bool flag;
map<char,int> piror;
map<string,string> data;
map<string,bool> isdefined,vis;
bool isletter(char c){ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
bool issymbol(char c){ return (c == '+' || c == '-' || c == '*' || c == '(' || c == ')'); }
int calculator(string expre){
if(!flag) return 0;
stack<int> num;
stack<char> symbol;
for(int i = 0;i < expre.length();i++){
if(isdigit(expre[i])){
int x = 0;
while(i < expre.length() && isdigit(expre[i])){
x = x*10 + expre[i] - '0';
i++;
}
i--;
num.push(x);
}else if(isletter(expre[i])){
string tmp;
while(i < expre.length() && ( isdigit(expre[i]) || isletter(expre[i]) ) ){
tmp += expre[i];
i++;
}
i--;
if(!isdefined[tmp] || vis[tmp] == true){
flag = false;
return 0;
}
vis[tmp] = true;
num.push(calculator(data[tmp]));
}else if(issymbol(expre[i])){
if(symbol.empty()) symbol.push(expre[i]);
else if(expre[i] == '(') symbol.push(expre[i]);
else if(expre[i] == ')'){
while(!symbol.empty() && symbol.top() != '('){
char c = symbol.top(); symbol.pop();
int b = num.top(); num.pop();
int a = num.top(); num.pop();
if(c == '+') num.push(a + b);
else if(c == '-') num.push(a - b);
else if(c == '*') num.push(a * b);
}
if(!symbol.empty()) symbol.pop();
}else{
while(!symbol.empty() && piror[symbol.top()] >= piror[expre[i]]){
char c = symbol.top(); symbol.pop();
int b = num.top(); num.pop();
int a = num.top(); num.pop();
if(c == '+') num.push(a + b);
else if(c == '-') num.push(a - b);
else if(c == '*') num.push(a * b);
}
symbol.push(expre[i]);
}
}
}
while(!symbol.empty()){
char c = symbol.top(); symbol.pop();
int b = num.top(); num.pop();
int a = num.top(); num.pop();
if(c == '+') num.push(a + b);
else if(c == '-') num.push(a - b);
else if(c == '*') num.push(a * b);
}
return num.top();
}
那简单计算器部分就这样结束了,我们再来写主函数。题目中有三种语句:复制,输出和清空。我们按照题目的意思依次读入字符串,进行处理。
对于清空,只需要将记录变量是否被定义的数组清空和记录变量名称的数组清空就可以了。
对于输出,对需要输出的变量的表达式调用上面的简单计算器就可以了。
对于赋值,把 := 号后面的柿子记录到记录变量表达式的 map 数组中去就可以了。
所以我们可以写出这样的主函数:
int main(){
piror['*'] = piror['/'] = 2;
piror['+'] = piror['-'] = 1;
piror['('] = 0;
piror[')'] = 3;
while(getline(cin,s)){
int cnt = 0;
for(int i = 0;i < s.length();i++){
if(s[i] == ' ' || s[i] == '\n') continue;
cnt++; while(i < s.length() && s[i] != ' ') code[cnt] += s[i], i++;
i--;
}
if(code[1] == "RESET"){
for(int i = 0;i < var.size();i++){
isdefined[var[i]] = false;
data[var[i]] = "";
}
}else if(code[1] == "PRINT"){
if(!isdefined[code[2]]) printf("UNDEF\n");
else{
flag = true;
int ans = calculator(data[code[2]]);
if(flag == false) printf("UNDEF\n");
printf("%d\n",ans);
}
}else{
if(!isdefined[code[1]]){
isdefined[code[1]] = true;
var.push_back(code[1]);
}
data[code[1]] = "";
for(int i = 3;i <= cnt;i++){
data[code[1]] += code[i];
}
// 处理负数的情况
if(data[code[1]][0] == '-') data[code[1]] = "0" + data[code[1]];
}
for(int i = 1;i <= cnt;i++) code[i] = "";
}
return 0;
}
看上去很正确,实在是完美无缺,对不对?
看到上面这句话,你肯定知道我又要举出一个反例来了:
a := -5--3
PRINT a
你肯定会说,这样的表达式不合法,确实,在日常生活中,这样的表达式是不存在的,但是你仔细阅读一幕意思,这种表达式违反了那一条规定呢?
所以我们依旧需要处理这种情况。可以考虑如果要两个连续的符号出现,我们可以在中间填上一个 \(0\),并将后面加上括号。对于上面的例子,处理完的结果就是:
a := -5-(0-3)
这样就可以正常计算了,所以我们的主函数又要有一番修改:
bool flag;
string s,code[1010];
vector<string> var;
map<char,int> piror;
map<string,string> data;
map<string,bool> isdefined;
int main(){
piror['*'] = piror['/'] = 2;
piror['+'] = piror['-'] = 1;
piror['('] = 0;
piror[')'] = 3;
while(getline(cin,s)){
int cnt = 0;
for(int i = 0;i < s.length();i++){
if(s[i] == ' ' || s[i] == '\n') continue;
cnt++; while(i < s.length() && s[i] != ' ') code[cnt] += s[i], i++;
i--;
}
if(code[1] == "RESET"){
for(int i = 0;i < var.size();i++){
isdefined[var[i]] = false;
data[var[i]] = "";
}
}else if(code[1] == "PRINT"){
if(!isdefined[code[2]]) printf("UNDEF\n");
else{
flag = true;
int ans = calculator(data[code[2]]);
if(flag == false) printf("UNDEF\n");
printf("%d\n",ans);
}
}else{
if(!isdefined[code[1]]){
isdefined[code[1]] = true;
var.push_back(code[1]);
}
data[code[1]] = "";
for(int i = 3;i <= cnt;i++){
data[code[1]] += code[i];
}
if(data[code[1]][0] == '-') data[code[1]] = "0" + data[code[1]];
}
for(int i = 1;i <= cnt;i++) code[i] = "";
}
return 0;
}
这样处理后就结束了(真的是恶心的数据),这次是真的结束了。
PART 3 完整代码
前面其实陆陆续续已经把代码放出来了,这里放完整的出来:
#include<bits/stdc++.h>
using namespace std;
bool flag;
string s,code[1010];
vector<string> var;
map<char,int> piror;
map<string,string> data;
map<string,bool> isdefined,vis;
bool isletter(char c){ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
bool issymbol(char c){ return (c == '+' || c == '-' || c == '*' || c == '(' || c == ')'); }
int calculator(string expre){
if(!flag) return 0;
stack<int> num;
stack<char> symbol;
for(int i = 0;i < expre.length();i++){
if(isdigit(expre[i])){
int x = 0;
while(i < expre.length() && isdigit(expre[i])){
x = x*10 + expre[i] - '0';
i++;
}
i--;
num.push(x);
}else if(isletter(expre[i])){
string tmp;
while(i < expre.length() && ( isdigit(expre[i]) || isletter(expre[i]) ) ){
tmp += expre[i];
i++;
}
i--;
if(!isdefined[tmp] || vis[tmp] == true){
flag = false;
return 0;
}
vis[tmp] = true;
num.push(calculator(data[tmp]));
}else if(issymbol(expre[i])){
if(symbol.empty()) symbol.push(expre[i]);
else if(expre[i] == '(') symbol.push(expre[i]);
else if(expre[i] == ')'){
while(!symbol.empty() && symbol.top() != '('){
char c = symbol.top(); symbol.pop();
int b = num.top(); num.pop();
int a = num.top(); num.pop();
if(c == '+') num.push(a + b);
else if(c == '-') num.push(a - b);
else if(c == '*') num.push(a * b);
}
if(!symbol.empty()) symbol.pop();
}else{
while(!symbol.empty() && piror[symbol.top()] >= piror[expre[i]]){
char c = symbol.top(); symbol.pop();
int b = num.top(); num.pop();
int a = num.top(); num.pop();
if(c == '+') num.push(a + b);
else if(c == '-') num.push(a - b);
else if(c == '*') num.push(a * b);
}
symbol.push(expre[i]);
}
}
}
while(!symbol.empty()){
char c = symbol.top(); symbol.pop();
int b = num.top(); num.pop();
int a = num.top(); num.pop();
if(c == '+') num.push(a + b);
else if(c == '-') num.push(a - b);
else if(c == '*') num.push(a * b);
}
return num.top();
}
int main(){
piror['*'] = piror['/'] = 2;
piror['+'] = piror['-'] = 1;
piror['('] = 0;
piror[')'] = 3;
while(getline(cin,s)){
int cnt = 0;
bool QWQ = true;
for(int i = 0;i < s.length();i++){
if(s[i] == ' ' || s[i] == '\n') continue;
cnt++;
if(isletter(s[i])){
while(i < s.length() && ( isdigit(s[i]) || isletter(s[i]) ) ){
code[cnt] += s[i];
i++;
}
if(QWQ == false){
code[++cnt] += ")";
QWQ = true;
}
i--;
}else if(isdigit(s[i])){
while(i < s.length() && isdigit(s[i]) ){
code[cnt] += s[i];
i++;
}
if(QWQ == false){
code[++cnt] += ")";
QWQ = true;
}
i--;
}else if(s[i] == ':'){
code[cnt] += ":=";
i++;
}else if(issymbol(s[i])){
if(issymbol(code[cnt-1][0]) && code[cnt-1] != ")" && code[cnt-1] != "(" && s[i] != '(' && s[i] != ')'){
code[cnt++] += "("; code[cnt++] += "0";
QWQ = false;
}
code[cnt] += s[i];
}
}
if(code[1] == "RESET"){
for(int i = 0;i < var.size();i++){
isdefined[var[i]] = false;
data[var[i]] = "";
}
var.clear();
}else if(code[1] == "PRINT"){
if(!isdefined[code[2]]) printf("UNDEF\n");
else{
flag = true;
vis[code[2]] = true;
int ans = calculator(data[code[2]]);
if(flag == false) printf("UNDEF\n");
else printf("%d\n",ans);
for(int i = 0;i < var.size();i++) vis[var[i]] = false;
}
}else{
if(!isdefined[code[1]]){
isdefined[code[1]] = true;
var.push_back(code[1]);
}
data[code[1]] = "";
for(int i = 3;i <= cnt;i++){
if(code[i] == ":=") continue;
data[code[1]] += code[i];
}
if(data[code[1]][0] == '-') data[code[1]] = "0" + data[code[1]];
}
for(int i = 1;i <= cnt;i++) code[i] = "";
}
return 0;
}
PART 4:幕后花絮




浙公网安备 33010602011771号