zoukankan      html  css  js  c++  java
  • c++编写的算24程序

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

    截图

    2

  • 相关阅读:
    【prufer编码】BZOJ1430 小猴打架
    【费马小定理】BZOJ3260 跳
    【欧拉函数】BZOJ2705: [SDOI2012]Longge的问题
    【卡特兰数】BZOJ1485: [HNOI2009]有趣的数列
    【缩点+拓扑判链】POJ2762 Going from u to v or from v to u?
    【Floyd】BZOJ1491: [NOI2007]社交网络
    【转】对信息学竞赛中调试方法的建议
    【建图+拓扑判环】BZOJ3953: [WF2013]Self-Assembly
    【dfs判负环】BZOJ1489: [HNOI2009]最小圈
    【二分+最小树形图】UVA11865 比赛网络
  • 原文地址:https://www.cnblogs.com/speedmancs/p/1716622.html
Copyright © 2011-2022 走看看