现在的位置: 首页 > 综合 > 正文

24点游戏

2013年10月11日 ⁄ 综合 ⁄ 共 6237字 ⁄ 字号 评论关闭

24点是一种老少皆宜的游戏,它的具体玩法如下:

给玩家4张牌,每张牌的面值都在1---13之间,允许其中有数值相同的牌。采用加、减、乘、除四则运算,允许中间运算存在小数,并且可以使用括号,但每张牌只能使用一次,尝试构造一个多项式,使其运算结果为24.

输入:n1,n2,n3,n4。

输出:若能得到运算结果为24,则输出一个对应的计算表达式。

输入:11,8,3,5

输出:(11-8)*(3+5) = 24

分析与解法

最直接的想法就是采用穷举法,因为运算符号只有4种,每个数字只能使用一次,所以通过穷举4个数所有可能的表达式,并分别计算出各表达式的值,就可以得到答案。那么如何穷举所有可能的表达式呢?

先不考虑使用括号,可以做出如下分析:

每个数只能使用一次,对4个整数进行全排列,总共有4!=4*3*2*1=24中排列。4个数的四则运算中总共需要3个运算符,同一运算符可以重复出现,那么对于每一个排列,总共可有4*4*4中表达式。因此在不考虑括号的情况下,总共可以得到4!*4*4*4=1536中表达式。

接下来再考虑加上括号后的情况,对于4个数而言,总共会有以下5种加括号的方式:

(A(B(CD)))、(A((BC)D))、((AB)(CD))、((A(BC))D)、(((AB)C)D)。

所以需要遍历的表达式数最多有4!*4*4*4*5=7680种。即使采用逆波兰表达式,其总数也不变。

通过上面的分析,得到了一种解24点的基本思路,即遍历运算符、数字和括号的所有排列组合形式,接下来,我们将更加细致地讨论这种解法的一个具体实现。

假设给定的4个数组成的集合为A={1,2,3,4},定义函数f(A)为对集合A中的元素进行所有可能的四则混合运算所得到的值。

首先从集合A中任意取出两个数,如取出1和2,A=A-{1,2},对取出来的数分别进行不同的四则运算,1+2=3,1-2=-1,1/2=0.5,1*2=2,将得到的结果再分别加入集合A,可得到B={3,3,4},C={-1,3,4},D={0.5,3,4},E={2,3,4}四个新的集合,那么f(A)= f(B)+f(C)+f(D)+f(E),通过以上的计算就达到了分而治之的目的,问题规模就从4个数降到了3个数,成了3个数的4个子问题之和。综上所述,可以得到递归解法如下:

将给定的4个数放入数组Array中,将其作为参数传入函数f中,伪代码如下所示:

  1. f(Array)  
  2. {  
  3.     if(Array.Length < 2)  
  4.     {  
  5.         if(得到的最终结果为)  
  6.             输出表达式  
  7.         else   输出无法构造符合要求的表达式  
  8.     }  
  9.     foreach(从数组中任取两个数的组合)  
  10.     {  
  11.         foreach(运算符(+, -, *, /))  
  12.         {  
  13.             1、计算该组合在此运算符下的结果  
  14.             2、将该组合中的两个数从原数组中移除,并将步骤的计算结果放入数组  
  15.             3、对新数组递归调用f。如果找到一个表达式则返回  
  16.             4、将步骤的计算结果移除,并将该组合中的两个数重新放回数组中对应的位置  
  17.         }  
  18.     }  
  19. }  

具体代码如下所示:

  1. #include "iostream"
      
  2. #include "cmath"
      
  3. #include "string"
      
  4. using namespace std;  
  5.   
  6. const double Threshold = 1E-6;      //容差值
      
  7. const int CardsNumber = 4;  
  8. const int ResultValue = 24;  
  9. double number[CardsNumber];         //输入的4个整数
      
  10. string result[CardsNumber];         //保存表达式
      
  11.   
  12. bool PointsGame(int n)  
  13. {  
  14.     if(n == 1)  
  15.     {  
  16.         //由于浮点数运算会有精度误差,所以用一个很小的数1E-6来做容差值
      
  17.         if(fabs(number[0] - ResultValue) < Threshold)  
  18.         {  
  19.             cout << result[0] << " == 24" << endl;  
  20.             return true;  
  21.         }  
  22.         else  
  23.         {  
  24.             return false;  
  25.         }  
  26.     }  
  27.     for(int i = 0; i < n; i++)  
  28.     {  
  29.         for(int j = i + 1; j < n; j++)     //从数组中任取两个数的组合
      
  30.         {  
  31.             double a, b;  
  32.             string expa, expb;  
  33.   
  34.             a = number[i];  
  35.             b = number[j];  
  36.             number[j] = number[n-1];     //任取两个数并将其结果放入数组后,原数组的个数减少一个
      
  37.   
  38.             expa = result[i];            //保存表达式中的数字
      
  39.             expb = result[j];  
  40.             result[j] = result[n-1];  
  41.   
  42.             result[i] = '(' + expa + '+' + expb + ')';      //任取的两个数在“+”运算符下的结果
      
  43.             number[i] = a + b;                              //将该运算符下的运算结果放入数组中
      
  44.             if(PointsGame(n - 1))  
  45.                 return true;  
  46.   
  47.             result[i] = '(' + expa + '-' + expb + ')';      //任取的两个数在“-”运算符下的结果
      
  48.             number[i] = a - b;  
  49.             if(PointsGame(n - 1))  
  50.                 return true;  
  51.   
  52.             result[i] = '(' + expb + '-' + expa + ')';      //任取的两个数在“-”运算符下的结果
      
  53.             number[i] = b - a;  
  54.             if(PointsGame(n - 1))  
  55.                 return true;  
  56.   
  57.             result[i] = '(' + expa + '*' + expb + ')';      //任取的两个数在“*”运算符下的结果
      
  58.             number[i] = a * b;  
  59.             if(PointsGame(n - 1))  
  60.                 return true;  
  61.             if(b != 0)  
  62.             {  
  63.                 result[i] = '(' + expa + '/' + expb + ')';  //任取的两个数在“/”运算符下的结果
      
  64.                 number[i] = a / b;  
  65.                 if(PointsGame(n - 1))  
  66.                     return true;  
  67.             }  
  68.             if(a != 0)  
  69.             {  
  70.                 result[i] = '(' + expb + '/' + expa + ')';  //任取的两个数在“/”运算符下的结果
      
  71.                 number[i] = b / a;  
  72.                 if(PointsGame(n - 1))  
  73.                     return true;  
  74.             }  
  75.             number[i] = a;    //将刚开始任取的两个数重新放回数组中对应的位置
      
  76.             number[j] = b;  
  77.             result[i] = expa;  
  78.             result[j] = expb;  
  79.         }  
  80.     }  
  81.     return false;  
  82. }  
  83.   
  84. int main(void)  
  85. {  
  86.     int i,x;  
  87.     for(i = 0; i < CardsNumber; i++)  
  88.     {  
  89.         char buffer[20];  
  90.         cout << "Please input the " << i+1 << "th number:";  
  91.         cin >> x;  
  92.         number[i] = x;            //保存输入的4个整数的浮点型数组
      
  93.         itoa(x, buffer, 10);      //把输入的4个整数按10进制转化为字符串
      
  94.         result[i] = buffer;  
  95.     }  
  96.     if(PointsGame(CardsNumber) )  
  97.     {  
  98.         cout << "Success." << endl;  
  99.     }  
  100.     else  
  101.     {  
  102.         cout << "Fail." << endl;  
  103.     }  
  104.     system("pause");  
  105.     return 0;  
  106. }  

运行效果图如下:

 

抱歉!评论已关闭.