C++实现24点游戏

1.   算法思路

(1)首先穷举的可行性问题。我把表达式如下分成三类
1
、 无括号的简单表达式。
2、 有一个括号的简单表达式。
3、 有两个括号的较复4,杂表达式。
穷举的开始我对给出的四个数进行排列,其可能的种数为4*3*2*1=24。我利用一个嵌套函数实现四个数的排列

     (2)那么作为栈的著名应用,表达式的计算可以有两种方法。

第一种方法: 
  首先建立两个栈,操作数栈OVS和运算符栈OPS。其中,操作数栈用来记忆表达式中的操作数,其栈顶指针为topv,初始时为空,即topv=0;运算符栈用来记忆表达式中的运算符,其栈顶指针为topp,初始时,栈中只有一个表达式结束符,即topp=1,且OPS1=‘。此处的即表达式结束符。
  然后自左至右的扫描待处理的表达式,并假设当前扫描到的符号为W,根据不同的符号W做如下不同的处理:
1W为操作数
2则将W压入操作数栈OVS 
3且继续扫描下一个字符
4W为运算符
5则根据运算符的性质做相应的处理:
(1)、若运算符为左括号或者运算符的优先级大于运算符栈栈顶的运算符(OPS(top)),则将运算符W压入运算符栈OPS,并继续扫描下一个字符。
(2)、若运算符W为表达式结束符且运算符栈栈顶的运算符也为表达式结束符(即OPS(topp)=’;’),则处理过程结束,此时,操作数栈栈顶元素(即OVStopv))即为表达式的值。
(3)、若运算符W为右括号且运算符栈栈顶的运算符为左括号(即OPStopp=’(‘),则将左括号从运算符栈谈出,且继续扫描下一个符号。
(4)、若运算符的右不大于运算符栈栈顶的运算符(即OPS(topp)),则从操作数栈OVS中弹出两个操作数,设先后弹出的操作数为ab,再从运算符栈OPS中弹出一个运算符,设为+,然后作运算a+b,并将运算结果压入操作数栈OVS。本次的运算符下次将重新考虑。
第二种方法—— 
  首先对表达式进行线性化,然后将线性表达式转换成机器指令序列以便进行求值。
  那么什么是表达式的线性化呢?人们所习惯的表达式的表达方法称为中缀表示。中缀表示的特点是运算符位于运算对象的中间。但这种表示方式,有时必须借助括号才能将运算顺序表达清楚,而且处理也比较复杂。
   1929年,波兰逻辑学家Lukasiewicz提出一种不用括号的逻辑符号体系,后来人们称之为波兰表示法(Polish notation)。波兰表达式的特点是运算符位于运算对象的后面,因此称为后缀表示。在对波兰表达式进行运算,严格按照自左至右的顺序进行。下面给出一些表达式及其相应的波兰表达式。
表达式波兰表达式
A-B AB- 
A-B*C+D AB-C*D+ 
A*B+C/D-E*F ABCD/+*EF*- 
B+C/A-D BC+AD-/ 

  OK,所谓表达式的线性化是指将中缀表达的表达式转化为波兰表达式。对于每一个表达式,利用栈可以把表达式变换成波兰表达式,也可以利用栈来计算波兰表达式的值

下面是代码

//西安科技大学-计算机科学与技术学院
#include <iostream>  
#include <string>  
#include <cmath>  
   
using namespace std;  
   
const  double  PRECISION = 1E-6;  
const  int  COUNT_OF_NUMBER  = 4;  
const  int  NUMBER_TO_BE_CAL = 24;  
double  number[COUNT_OF_NUMBER];  
string  expression[COUNT_OF_NUMBER];  
bool Judgement = false;                    //判断是否有解。
int count = 0;   
   
void  Search(int   n)  
{  
   if (n   ==   1)
   {  
     if ( fabs(number[0] - NUMBER_TO_BE_CAL) <= PRECISION   )  //对于除法,要小心小数的精确位数
	 {  
     cout << expression[0] << "\t\t"; 
     Judgement = true;
     count ++;
     if((count % 3)==0)
     cout<<endl;
     }  
   else 
   { }
   
   }  
   
   for(int i=0;  i < n; i++)
    {
      for (int j = i + 1; j < n; j++)
    {  
       double   a,   b;  
       string   expa,   expb;  
	   a   =   number[i];  
       b   =   number[j];  
       number[j]  =  number[n - 1];   //递归之后,n比以前小一位,所以可以不停向前赋值 
	   expa   =   expression[i];  
       expb   =   expression[j];  
       expression[j]  =  expression[n - 1];   //递归之后,n比以前小一位,所以可以不停向前赋值
	   expression[i]   =   '('   +   expa   +   '+'   +   expb   +   ')';   //加法不需要分顺序
       number[i]   =   a   +   b;  
       Search(n-1);
       expression[i]   =   '('   +   expa   +   '-'   +   expb   +   ')';   //减法应该分顺序,减数以及被减数
       number[i]   =   a   -   b;  
       Search(n-1); 
	   expression[i]   =   '('   +   expb   +   '-'   +   expa   +   ')';   //减法应该分顺序,减数以及被减数
       number[i]   =   b   -   a;  
       Search(n-1); 
	   expression[i]   =   '('   +   expa   +   '*'   +   expb   +   ')';   //乘法不需要分顺序
       number[i]   =   a   *   b;  
       Search(n-1); 
	   if (b != 0)
      {  
         expression[i]   =   '('   +   expa   +   '/'   +   expb   +   ')';   //除法应该分顺序,除数以及被除数
         number[i] = a / b;  
         Search(n-1);  
      }    
       if (a != 0)
      {  
         expression[i]   =   '('   +   expb   +   '/'   +   expa   +   ')';   //除法应该分顺序,除数以及被除数
         number[i]   =   b  /  a;  
         Search(n-1);  
      }  
   
         number[i]   =   a;                  //这4句语句是为了防止如果上面几种可能都失败了的话,
         number[j]   =   b;                  //就把原来的赋值撤消回去,以无干扰的正确的进入到下一次
         expression[i]   =   expa;           //for循环队列中。
         expression[j]   =   expb;           //
      }  
    }
}  
   
int  main()  
{  
  cout<<"请依次输入4个数字:\n";
  for (int i = 0; i < COUNT_OF_NUMBER; i++) 
  {  
    char   buffer[20];   
    cout<<"第"<<i+1<<"个卡片:";
    cin   >>   number[i];                  
    itoa(number[i],   buffer,   10);   //itoa()函数的作用是把第一个参数(数值)传送到第二个参数(字符串)中去,第三个
               //参数(int型)是该数值在字符串里以什么进制存放。
    expression[i]   =   buffer;  
  }
  cout<<endl;
  Search(COUNT_OF_NUMBER) ;
  if(Judgement==true)
  {  
    cout   <<   "\n成功"   <<   endl;
    cout<<"所以可以计算的次数总和 = "<<count<<endl;
  }  
  else
  {  
    cout   <<   "失败"   <<   endl;  
  }       
  system("pause");
  return 0;
}

  • 11
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值