今天在大本营看到了一个人家写的24程序,感觉人家的思路非常清晰,于是自己也着手写了一个,顺便温习了一下标准c++。在我的程序中,使用了stringstream类做parse,后缀表达式穷举所有可行解,而用中缀表达式计算用户输入的表达式。因为比较懒,当用户算不出来时,按出来的答案是个后缀表达式。
===============================================
4.21更新
1.发现对后缀表达式的理解错了。修改了穷举算24的函数,穷举空间改为4!*4^3 * 5,其中3个操作符在长度为7的后缀表达式的可能位置为5种。
2.使用二叉树和堆栈实现了后缀表达式向中缀的转化,可以给出算24的答案
3.加入了接受用户输入的4个数,也可以让系统产生四个数。
功能上已经比较完整了,用户界面上还不是很好,不过这个已经不是很重要的问题的。这部分代码,如果要做
图形界面,应该是很好移过去的。
有关源代码如下:
Token.h :用于Parse,以及操作符,优先级的定义
- #ifndef TOKEN_H
- #define TOKEN_H
- enum Token_type {Numeric = 0,Op};
- enum Operator{ADD_OPR = 0,MINUS_OPR,
- MUL_OPR,DIV_OPR,
- LEFT_BRA,RIGHT_BRA,
- TER};
- enum PRI{HIGHER = 0,LOWER,EQUAL,NO_POSSIBLE};
- class Token
- {
- public:
- Token(){}
- Token(Token_type _type,double _x,Operator _op):type(_type),
- x(_x),op(_op){}
- Token_type type;
- double x;
- Operator op;
- };
- void Parse(string expression,vector<Token>& tokens);
- bool isOp(char c);
- #endif
Token.cpp
- #include <string>
- #include <vector>
- #include <sstream>
- using namespace std;
- #include "Token.h"
- extern int OpTypeNum;
- char operators[7] = {'+','-','*','/','(',')','#'};
- bool isOp(char c,int &index)
- {
- for (int i = 0;i < OpTypeNum;i++)
- {
- if (c == operators[i])
- {
- index = i;
- return true;
- }
- }
- return false;
- }
- void Parse(string expression,vector<Token>& tokens)
- {
- stringstream ss (stringstream::in | stringstream::out);
- ss << expression;
- char c;
- int val,index;
- while (ss >> c)
- {
- if (isdigit(c))
- {
- ss.putback(c);
- ss >> val;
- tokens.push_back(Token(Numeric,val,Operator(0)));
- }else if (isOp(c,index))
- tokens.push_back(Token(Op,-1,Operator(index)));
- }
- }
ExpCalc.h 用堆栈实现的中缀和后缀表达式的计算,以及后缀转化为中缀表达式的功能
- #ifndef EXPCALC_H
- #define EXPCALC_H
- class tree_Node
- {
- public:
- tree_Node(){}
- ~tree_Node();
- void Print();
- tree_Node(tree_Node * _left,tree_Node * _right,Token _token):
- left(_left),right(_right),token(_token){}
- tree_Node * left;
- tree_Node * right;
- Token token;
- };
- class ExpCalc
- {
- public:
- void ShowInfixExp(vector<Token>& tokens);
- bool PostfixCalc(vector<Token> & tokens,double & res);
- bool infixCalc(vector<Token>& tokens,double& res);
- bool Calc(double x1,double x2,Operator op,double & res);
- void Clear();
- private:
- bool doWhenHigher(Operator op);
- bool doWhenLower(Operator op,Operator nxt_op);
- bool doWhenEqual();
- stack<double> operands_stack;
- stack<Operator> operators_stack;
- };
- #endif
ExpCalc.cpp
- #include <stack>
- #include <vector>
- #include <cmath>
- #include <iostream>
- using namespace std;
- #include "Token.h"
- #include "ExpCalc.h"
- #include "24Game.h"
- int OpTypeNum = 7;
- PRI operatorPRIs[7][7] ={{HIGHER,HIGHER,LOWER,LOWER,LOWER,HIGHER,HIGHER},
- {HIGHER,HIGHER,LOWER,LOWER,LOWER,HIGHER,HIGHER},
- {HIGHER,HIGHER,HIGHER,HIGHER,LOWER,HIGHER,HIGHER},
- {HIGHER,HIGHER,HIGHER,HIGHER,LOWER,HIGHER,HIGHER},
- {LOWER,LOWER,LOWER,LOWER,LOWER,EQUAL,NO_POSSIBLE},
- {NO_POSSIBLE,NO_POSSIBLE,NO_POSSIBLE,NO_POSSIBLE,/
- NO_POSSIBLE,NO_POSSIBLE,NO_POSSIBLE},
- {LOWER,LOWER,LOWER,LOWER,LOWER,NO_POSSIBLE,EQUAL}};
- extern char operators[7];
- bool ExpCalc::Calc(double x1,double x2,Operator op,double& res)
- {
- bool flag = true;
- switch(op)
- {
- case ADD_OPR:
- res = x1 + x2;
- break;
- case MINUS_OPR:
- res = x1 - x2;
- break;
- case MUL_OPR:
- res = x1 * x2;
- break;
- case DIV_OPR:
- if (fabs(x2) < 1e-6)
- flag = false;
- else
- res = x1 / x2;
- break;
- default:
- flag = false;
- }
- return flag;
- }
- bool ExpCalc::PostfixCalc(vector<Token> &tokens,double& res)
- {
- Clear();
- for (int i = 0;i<tokens.size();i++)
- {
- if (tokens[i].type == Numeric)
- operands_stack.push(tokens[i].x);
- else
- {
- if (operands_stack.size()>=2)
- {
- double x2 = operands_stack.top();
- operands_stack.pop();
- double x1 = operands_stack.top();
- operands_stack.pop();
- double r;
- bool isOk = Calc(x1,x2,tokens[i].op,r);
- if (!isOk) return false;
- operands_stack.push(r);
- }else{
- return false;
- }
- }
- }
- if (operands_stack.size()!=1)
- return false;
- res = operands_stack.top();
- return true;
- }
- void ExpCalc::Clear()
- {
- while (!operands_stack.empty())
- operands_stack.pop();
- while (!operators_stack.empty())
- operators_stack.pop();
- }
- bool ExpCalc::doWhenHigher(Operator op)
- {
- if (operands_stack.size()<2)
- return false;
- double x2 = operands_stack.top();
- operands_stack.pop();
- double x1 = operands_stack.top();
- operands_stack.pop();
- double res;
- bool isOk = Calc(x1,x2,op,res);
- if (!isOk) return false;
- operands_stack.push(res);
- operators_stack.pop();
- return true;
- }
- bool ExpCalc::doWhenLower(Operator op,Operator nxt_op)
- {
- operators_stack.push(nxt_op);
- return true;
- }
- bool ExpCalc::doWhenEqual()
- {
- if (operators_stack.empty())
- return false;
- operators_stack.pop();
- return true;
- }
- bool ExpCalc::infixCalc(vector<Token>& tokens,double& res)
- {
- Clear();
- operators_stack.push(TER);
- double x1,x2;
- bool isOk;
- for (int i = 0;i<tokens.size();i++)
- {
- if (tokens[i].type == Numeric)
- {
- operands_stack.push(tokens[i].x);
- }else{
- if (operators_stack.empty())
- {
- return false;
- }
- Operator nxt_op = tokens[i].op;
- bool over = false;
- while (!operators_stack.empty())
- {
- Operator op = operators_stack.top();
- PRI pri = operatorPRIs[int(op)][int(nxt_op)];
- switch(pri)
- {
- case HIGHER:
- isOk = doWhenHigher(op);
- break;
- case LOWER:
- isOk = doWhenLower(op,nxt_op);
- over = true;
- break;
- case EQUAL:
- isOk = doWhenEqual();
- over = true;
- break;
- case NO_POSSIBLE:
- return false;
- default:
- return false;
- }
- if (!isOk)
- return false;
- if(over)
- break;
- }
- }
- }
- if (!operators_stack.empty() || operands_stack.size()!=1)
- return false;
- res = operands_stack.top();
- return true;
- }
- void ExpCalc::ShowInfixExp(vector<Token>& tokens)
- {
- stack<tree_Node *> tn_stack;
- tree_Node * root = NULL;
- for (int i = 0;i<tokens.size();i++)
- {
- if (tokens[i].type == Numeric)
- {
- tn_stack.push(new tree_Node(NULL,NULL,tokens[i]));
- }else{
- if (tn_stack.size()<2)
- {
- cout << "后缀表达式有错!"<<endl;
- return;
- }
- tree_Node * x2 = tn_stack.top();
- tn_stack.pop();
- tree_Node * x1 = tn_stack.top();
- tn_stack.pop();
- double res;
- bool isOk = Calc(x1->token.x,x2->token.x,tokens[i].op,res);
- if (!isOk)
- {
- cout << "计算过程有误"<<endl;
- return;
- }
- root = new tree_Node(x1,x2,Token(Numeric,res,tokens[i].op));
- tn_stack.push(root);
- }
- }
- if (root == NULL)
- {
- cout << "后缀表达式有误"<<endl;
- return;
- }
- root->Print();
- cout << endl;
- delete root;
- }
- void tree_Node::Print()
- {
- if (left == NULL && right == NULL)
- {
- cout << token.x << " ";
- return;
- }
- cout << "(";
- if (left!=NULL)
- left->Print();
- cout << operators[token.op] << " ";
- if (right!=NULL)
- right->Print();
- cout << ")";
- }
- tree_Node::~tree_Node()
- {
- if (left!=NULL)
- {
- delete left;
- left = NULL;
- }
- if (right!=NULL)
- {
- delete right;
- right = NULL;
- }
- }
24Game.h 算24游戏的有关逻辑
- #ifndef GAME_H
- #define GAME_H
- class Game
- {
- public:
- Game();
- void GenNewNumbers();
- bool hasSolutions();
- void ShowNums();
- void ShowSolution();
- bool CheckInput(vector<int>& inputs);
- bool Calc(vector<Token>& tokens,double & res);
- bool Check(vector<Token>& tokens);
- static int minOperand;
- static int maxOperand;
- private:
- vector<Token> solu_tokens;
- bool hasSolu;
- string expression;
- ExpCalc calc;//表达式计算器
- int generated_operands[4];
- int input_operands[4];
- char input_operators[3];
- };
- #endif
24Game.cpp
- #include <stack>
- #include <time.h>
- #include <stdlib.h>
- #include <string>
- #include <algorithm>
- #include <vector>
- #include <cmath>
- #include <iostream>
- using namespace std;
- #include "Token.h"
- #include "ExpCalc.h"
- #include "24Game.h"
- int Game::minOperand = 1;
- int Game::maxOperand = 13;
- extern char operators[7];
- Game::Game()
- {
- srand(time(NULL));
- }
- bool Game::CheckInput(vector<int>& inputs)
- {
- for (int i = 0;i<4;i++)
- {
- generated_operands[i] = inputs[i];
- if (generated_operands[i] > maxOperand ||
- generated_operands[i] < minOperand)
- {
- return false;
- }
- }
- return true;
- }
- void Game::GenNewNumbers()
- {
- while (1)
- {
- for (int i = 0;i<4;i++)
- generated_operands[i] = rand()%(maxOperand-minOperand+1) + minOperand;
- if (hasSolutions()) break;
- }
- //generated_operands[0] = 4;
- //generated_operands[1] = 8;
- //generated_operands[2] = 4;
- //generated_operands[3] = 9;
- }
- //若有解,只保存一组先
- bool Game::hasSolutions()
- {
- vector<double> a(4);//操作数
- double res;
- copy(generated_operands,generated_operands+4,a.begin());
- sort(a.begin(),a.end());
- int operandPos[5][4] = {{0,1,2,3},{0,1,2,4},{0,1,2,5},{0,1,3,4},{0,1,3,5}};
- int opPos[5][3] = {{4,5,6},{3,5,6},{3,4,6},{2,5,6},{2,4,6}};
- vector<Token> tokens(7);
- while (next_permutation(a.begin(),a.end()))
- {
- for (int p = 0;p<5;p++)
- {
- for (int idx = 0;idx<4;idx++)
- {
- tokens[operandPos[p][idx]].type = Numeric;
- tokens[operandPos[p][idx]].x = a[idx];
- }
- for (int idx = 0;idx<3;idx++)
- tokens[opPos[p][idx]].type = Op;
- for (int i = ADD_OPR;i<=DIV_OPR;i++)
- {
- for (int j = ADD_OPR;j<=DIV_OPR;j++)
- {
- for (int k = ADD_OPR;k<=DIV_OPR;k++)
- {
- tokens[opPos[p][0]].op = Operator(i);
- tokens[opPos[p][1]].op = Operator(j);
- tokens[opPos[p][2]].op = Operator(k);
- bool isOk = calc.PostfixCalc(tokens,res);
- if (isOk && fabs(res - 24) < 1e-6)
- {
- solu_tokens = tokens;
- hasSolu = true;
- return true;
- }
- }
- }
- }
- }
- }
- hasSolu = false;
- return false;
- }
- void Game::ShowNums()
- {
- for (int i = 0;i<4;i++)
- cout << generated_operands[i] << " ";
- cout << endl;
- }
- void Game::ShowSolution()
- {
- calc.ShowInfixExp(solu_tokens);
- cout << endl;
- }
- bool Game::Calc(vector<Token>& tokens,double & res)
- {
- return calc.infixCalc(tokens,res);
- }
- bool Game::Check(vector<Token>& tokens)
- {
- vector<double> a;
- for (int i = 0;i<tokens.size();i++)
- {
- if (tokens[i].type == Numeric)
- a.push_back(tokens[i].x);
- }
- sort(a.begin(),a.end());
- vector<double> opr_copy(4);
- copy(generated_operands,generated_operands+4,opr_copy.begin());
- sort(opr_copy.begin(),opr_copy.end());
- if (opr_copy.size()!=a.size())
- {
- return false;
- }
- for (int i = 0;i<a.size();i++)
- {
- if (a[i]!=opr_copy[i])
- {
- return false;
- }
- }
- return true;
- }
24Points:主文件
- #include <string>
- #include <iostream>
- #include <vector>
- #include <stack>
- #include <cmath>
- using namespace std;
- #include "Token.h"
- #include "ExpCalc.h"
- #include "24Game.h"
- int main()
- {
- Game game;
- double res;
- while (1)
- {
- cout << "开始本轮算24游戏(输入g:系统随机产生测试数,输入s:用户自行输入4个要测试的数)"<<endl;
- char c;
- cin >> c;
- if (c == 'g')
- {
- game.GenNewNumbers();
- game.ShowNums();
- }else if(c == 's')
- {
- vector<int> inputs(4);
- cout << "输入四个1到13的整数,以空格隔开:";
- for (int i = 0;i<4;i++)
- cin >> inputs[i];
- bool isOk = game.CheckInput(inputs);
- if (!isOk){
- cout << "输入的数应在1到13之间"<<endl;
- continue;
- }
- if(!game.hasSolutions())
- {
- cout << "您的输入无解"<<endl;
- continue;
- }
- }else
- {
- cout << "无效命令"<<endl;
- continue;
- }
- cin.ignore();//吃掉回车键
- while(1)
- {
- cout << "输入你的表达式:以#结尾,只输入#将显示答案,案例:(4*9 -4-8)"<<endl;
- string expression;
- getline(cin,expression);
- if (expression == "#")
- {
- game.ShowSolution();
- break;
- }
- vector<Token> tokens;
- Parse(expression,tokens);
- if (!game.Check(tokens))
- {
- cout << "输入的数字不对"<<endl;
- continue;
- }
- bool isOk = game.Calc(tokens,res);
- if (!isOk)
- cout << "输入格式有误" <<endl;
- else if (fabs(res - 24) < 1e-6 )
- cout <<"您算对了"<<endl;
- else
- cout << "您算错了"<<endl;
- }
- }
- return 0;
- }
截图