今天在大本营看到了一个人家写的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; }
截图