要求:
输入4个数,判断能否通过加减乘除得到24;
用户给出表达式,判断用户的表达式的计算结果是否为24;
概要设计:
(1)求解24点,对于给出的4个数,判断是否能够通过加减乘除等运算得到24,如果能够得到24,说明这组数据有解,如果不能得到24,说明无解;
(2)表达式的解析计算,对于用户输入的表达式解析并进行计算,判断此表达式的运算结果是否为24;
详细设计:
1.求解24点
求解24点,这里主要用了回溯法,先将给出的4个数放到一个集合里,从集合中取出两个数来进行运算(+-*/),将运算的结果再放回集合,现在的问题就变成了对3个数求解24点,类此类推,最后集合里就只剩下一个数,判断这个数是否等于24,只要存在满足条件的组合方式,就说明4个数计算可以得到24。
2.表达式的计算
这是数学上经典的波兰式的计算的问题,对于这个问题,我们主要分为两个步骤,
(1)将波兰式(中缀表达式)化为逆波兰式(后缀表达式)
具体实现,我们需要用到堆栈和队列,这里队列是用来存储得到的逆波兰式的。算法思路如下:对于给定的波兰式,我们依次读取,如果是’(’,则入栈,如果是数字,则入队列,遇到运算符时,我们要把当前运算符和栈顶的运算符进行比较,如果当前运算符的优先级高则入栈,否则,栈内的运算符要出栈,进入队列,直到当前运算符优先级高于栈顶运算符,将当前运算符入栈,如果是当前运算符是‘)’,那就将栈内的元素出栈,进入队列,直到遇到‘(’为止,‘(’不要进入队列,最后,如果堆栈非空,将栈内元素运算符出栈,进入队列。
(2)计算逆波兰式的值
具体实现我们需要用到堆栈。算法思路:依次读取逆波兰式的每一位,如果是数字,直接入栈,如果是运算符,则将堆栈内最上面的两个元素出栈,进行运算,再将运算的结果入栈,这样计算得到的结果,就是我们所求的表示式的运算结果。
运行效果图:
代码:
#include <iostream>
#include <string>
#include <cmath>
#include <stack>
#include <queue>
#include <stdlib.h>
using namespace std;
const int cardNum = 4;
const double eps = 1e-6;
const double num = 24;
double number[cardNum];
string result[cardNum];
stack<double> sta;
queue<string> q;
bool isNumber(char c)
{
if(c >= '0' && c <= '9')
return true;
return false;
}
/*判断当前运算符优先级是否比栈顶元素优先级高,当前优先级高入栈*/
bool isHighPriority(const string &stackTop, const string &now)
{
if("#" == stackTop || "(" == stackTop)
{
return true;
}
if("*" == stackTop || "/" == stackTop)
{
return false;
}
else
{
if("*" == now || "/" == now)
{
return true;
}
}
return false;
}
/*后缀表达式计算*/
double caculate()
{
while(!q.empty())
{
if(isNumber(q.front().at(0)))
{
double tmp = atof(&(q.front().at(0)));
sta.push(tmp);
}
else
{
double bb = sta.top();
sta.pop();
double aa = sta.top();
sta.pop();
double cc;
switch(q.front().at(0))
{
case '+':
cc = aa + bb;
break;
case '-':
cc = aa - bb;
break;
case '*':
cc = aa * bb;
break;
case '/':
cc = aa / bb;
break;
default:
continue;
}
sta.push(cc);
}
q.pop();
}
return sta.top();
}
/*中缀表达式转后缀表达式*/
string s;
stack<string> stac;
void infixToSuffix()
{
stac.push("#");
for(int i = 0; i < s.length(); i++)
{
string ss;
if(isNumber(s[i]))
{
ss = "";
if(i != s.length() - 1 && isNumber(s[i+1]))
{
ss = ss + s[i] + s[i+1];
i++;
}
else
{
ss = ss + s[i];
}
q.push(ss);
}
else
{
ss = "";
ss = ss + s[i];
if(" " == ss)
{
continue;
}
if("(" == ss)
{
stac.push(ss);
continue;
}
if(")" == ss)
{
while("(" != stac.top())
{
q.push(stac.top());
stac.pop();
}
stac.pop();
continue;
}
if(isHighPriority(stac.top(), ss))
{
stac.push(ss);
}
else
{
while(true)
{
if(isHighPriority(stac.top(), ss))
{
break;
}
q.push(stac.top());
stac.pop();
}
stac.push(ss);
}
}
}
while(!stac.empty())
{
if("#" == stac.top())
{
break;
}
q.push(stac.top());
stac.pop();
}
}
/*判断是可以算到24点*/
bool game(int n)
{
if(1 == n)
{
if(fabs(num - number[0]) < eps)
{
// cout << result[0] << endl;
return true;
}
else
{
return false;
}
}
double a, b;
string stra, strb;
for(int i = 0; i < n - 1; i++)
{
for(int j = i + 1; j < n; j++)
{
a = number[i];
b = number[j];
number[j] = number[n - 1];
stra = result[i];
strb = result[j];
result[j] = result[n - 1];
/*+*/
number[i] = a + b;
result[i] = '(' + stra + '+' + strb + ')';
if(game(n - 1))
{
return true;
}
/*-*/
number[i] = a - b;
result[i] = '(' + stra + '-' + strb + ')';
if(game(n - 1))
{
return true;
}
number[i] = b - a;
result[i] = '(' + strb + '-' + stra + ')';
if(game(n - 1))
{
return true;
}
/***/
number[i] = a * b;
result[i] = '(' + stra + '*' + strb + ')';
if(game(n - 1))
{
return true;
}
/*/*/
if(0 != b)
{
number[i] = a / b;
result[i] = '(' + stra + '/' + strb + ')';
if(game(n - 1))
{
return true;
}
}
if(0 != a)
{
number[i] = b / a;
result[i] = '(' + strb + '/' + stra + ')';
if(game(n - 1))
{
return true;
}
}
number[i] = a;
number[j] = b;
result[i] = stra;
result[j] = strb;
}
}
return false;
}
int main()
{
for(int i = 0; i < cardNum; i++)
{
cin >> number[i];
result[i] = '0' + number[i];
}
if(game(cardNum))
{
cout << "Success" << endl;
cout << result[0] << endl;
}
else
{
cout << "Fail" << endl;
}
cin.ignore();
getline(cin, s);
infixToSuffix();
cout << caculate() << endl;
return 0;
}