应用题_24点求值

用暴力法求解24点


基本思路:

4个数进行四则运算有以下5种情况

((axb)yc)zd=
(axb)y(czd)=

(ax(byc))zd=
ax((byc)zd)=

ax(by(czd))=

a,b,c,d 属于 { x0 , x1 , x2 , x3 }
有 p = 4 * 3 * 2 * 1 = 24 种排列

x,y,z 属于 { + , - , * , / }
有 P = 4 * 4 * 4 = 64 种排列

所以一个24点问题共有 5 * 24 * 64 = 7680 个表达式
需要计算

计算每个表达式,与 goal = 24 进行比较,
如果相等,则该表达式是一种解法。

以下是我写的源程序


// 程序员:黄江斌
// 功能:求解24点问题,穷举策略
// 时间:11:36 2005-10-3
// 最后修改时间:11:36 2005-10-3

#include "iostream.h"
#include "string.h"
#include "math.h"

#define lineMax 256

void strFind( const char str[] , const char subStr[] , int &reti , int &retj )
{
 //在str中找首次出现subStr的位置,reti前,retj后
 //如果没有找到,返回 reti = -1 , retj = -1
 reti = -1;
 retj = -1;

 int i = 0;
 int j;
 while( str[i] != 0 )
 {
  while( str[i] != 0 && str[i] != subStr[0] )
  {
   i++;
  }
  if( str[i] == subStr[0] )
  {
   j = 1;
   while( str[i+j] != 0 && subStr[j] != 0 && str[i+j] == subStr[j] )
    j++;
   if( subStr[j] == 0 )
   {
    reti = i;
    retj = i + j - 1;
    return;
   }
  }
  i++;
 }
}

void strReplace( char str[] , const char subStr[] , char newStr[] )
{
 //str:被替换的串
 //subStr:要被替换的子串
 //newStr:用来替换的串
 int i , j;
 int subs , sube;
 char tempStr[lineMax] = { 0 };

 strFind( str , subStr , subs , sube );
 //没有找到子串,直接返回
 if( subs < 0 || sube < 0 )
  return;
 
 //原串除子串后的前段
 for( i = 0 ; i < subs ; i++ )
 {
  tempStr[i] = str[i];
 }
 //新串
 for( j = 0 ; newStr[j] != 0 ; i++ , j++ )
 {
  tempStr[i] = newStr[j];
 }
 //原串除子串后的后段
 for( j = sube + 1 ; str[j] != 0 ; i++ , j++ )
 {
  tempStr[i] = str[j];
 }
 for( i = 0 ; tempStr[i] != 0 ; i++ )
  str[i] = tempStr[i];
 str[i] = 0;
}

void double2str( char str[] , double n )
{
 //整数转化为字符串数
 long intPart = n;
 double floatPart;
 int index;

 int i , j = 0;

 //处理整数部分
 while( intPart != 0 )
 {
  str[j++] = intPart % 10 + '0';
  intPart /= 10;
 }
 
 index = j;

 //字符串反转
 char t;
 i = 0;
 j--;
 while( i < j )
 {
  t = str[i];
  str[i] = str[j];
  str[j] = t;
  i++;
  j--;
 }

 //处理小数部分
 intPart = n;
 floatPart = n - intPart;
 if( floatPart > 0 )
  str[index++] = '.';
 while( floatPart > 0 )
 {
  floatPart *= 10;
  intPart = floatPart;
  str[index++] = intPart + '0';
  floatPart -= intPart;
 }
 if( index == 0 )
  str[index++] = '0';
 str[index] = 0;
}

void char2str( char str[] , char ch )
{
 //将char转为char[]
 str[0] = ch;
 str[1] = 0;
}

void str2str( char str[] , const char str2[] )
{
 //字符串复制
 int i;
 for( i = 0 ; str2[i] != 0 ; i++ )
  str[i] = str2[i];
 str[i] = 0;
}

void solve( double num1 , double num2 , char op , double &ret )
{
 // 计算 ret = num1 op num2

 switch( op )
 {
 case '+':
  ret = num1 + num2;
  break;
 case '-':
  ret = num1 - num2;
  break;
 case '*':
  ret = num1 * num2;
  break;
 case '/':
  if( num2 != 0 )
   ret = num1 / num2;
  break;
 }
}

bool evalExpression( char exp[] , double &ret )
{
 //计算表达式的值

 //操作符栈
 char opStack[lineMax];
 int opTop = 0;
 //操作数栈
 double numStack[lineMax];
 int numTop = 0;

 //记录两数的运算结果
 double result = 0;
 double num1;
 double num2;
 char op;

 //读入表达式的字符
 char ch;
 //表达式当前的字符指针
 int p = 0;

 //构造数字时用到
 double t;

 opStack[0] = '#';
 opTop = 1;
 ch = exp[p++];
 while( ch != '=' )
 {
  if( ch >= '0' && ch <= '9' )
  {
  //读入数字
  //这里只把输入的double数据当作整数来处理
  //事实在算24点时,输入的数总是整数
  //如果要扩展,在这里进行修改
  //考虑输入数带小数部分的情况
   t = 0;
   while( ch >= '0' && ch <= '9' )
   {
    t = 10 * t + ( ch - '0' );
    ch = exp[p++];
   }
   numStack[numTop++] = t;
  }
  else if( ch == '(' )
  {
   opStack[opTop++] = ch;
   ch = exp[p++];
  }
  else if( ch == ')' )
  {
   //每次运算前检查是否有足够的操作数及操作符
   if( numTop <= 1 || opTop <= 1 )
    return false;
   num2 = numStack[--numTop];
   num1 = numStack[--numTop];
   op = opStack[--opTop];

   solve( num1 , num2 , op , result );

   numStack[numTop++] = result;

   //括号不匹配
   if( opStack[--opTop] != '(' )
    return false;

   ch = exp[p++];
  }
  else if( ( ch == '+' || ch == '-' ) && opStack[opTop-1] != '(' && opStack[opTop-1] != '#' )
  {
   if( numTop <= 1 || opTop <= 1 )
    return false;
   num2 = numStack[--numTop];
   num1 = numStack[--numTop];
   op = opStack[--opTop];

   solve( num1 , num2 , op , result );

   numStack[numTop++] = result;
  }
  else if( ( ch == '*' || ch == '/' ) && ( opStack[opTop-1] == '+' || opStack[opTop-1] == '-' ) )
  {
   opStack[opTop++] = ch;
   ch = exp[p++];
  }
  else if( ( ch == '*' || ch == '/' ) && ( opStack[opTop-1] == '*' || opStack[opTop-1] == '/' ) )
  {
   if( numTop <= 1 || opTop <= 1 )
    return false;
   num2 = numStack[--numTop];
   num1 = numStack[--numTop];
   op = opStack[--opTop];

   solve( num1 , num2 , op , result );

   numStack[numTop++] = result;
  }
  else
  {
   opStack[opTop++] = ch;
   ch = exp[p++];
  }// end if
 }// end while

 if( numTop <= 1 || opTop <= 1 )
  return false;

 num2 = numStack[--numTop];
 num1 = numStack[--numTop];
 op = opStack[--opTop];

 solve( num1 , num2 , op , result );

 numStack[numTop++] = result;

 ret = numStack[numTop-1];

 return true;
}

bool solve24( double num[] , int n , int goal , char expression[][lineMax] , int &total )
{
 //解决24点问题的主体部分

 //n:    total numbers
 //num[]:   the numbers
 //goal:    the result you want
 //expression[][]: return the expression

 int i;
 int a , b , c , d;
 int x , y , z;
 char num1[lineMax];
 char num2[lineMax];
 char num3[lineMax];
 char num4[lineMax];
 char op1[lineMax];
 char op2[lineMax];
 char op3[lineMax];
 char op[] = { '+' , '-' , '*' , '/' };
 char formula[][lineMax] = {
  "((axb)yc)zd=" ,
  "(axb)y(czd)=" ,
  "(ax(byc))zd=" ,
  "ax((byc)zd)=" ,
  "ax(by(czd))="
 };
 char exp[lineMax];
 double ret;
 total = 0;

 for( a = 0 ; a < 4 ; a++ )
 {
  for( b = 0 ; b < 4 ; b++ )
  {
   if( a != b )
   {
    for( c = 0 ; c < 4 ; c++ )
    {
     if( a != c && b != c )
     {
      d = 6 - a - b - c;
      // a b c d 是所给四个数的全排列

      double2str( num1 , num[a] );
      double2str( num2 , num[b] );
      double2str( num3 , num[c] );
      double2str( num4 , num[d] );
      for( x = 0 ; x < 4 ; x++ )
      {
       for( y = 0 ; y < 4 ; y++ )
       {
        for( z = 0 ; z < 4 ; z++ )
        {
         if( x != z && y != z )
         {
          // x y z 从四种操作符中选出三种的排列
          char2str( op1 , op[x] );
          char2str( op2 , op[y] );
          char2str( op3 , op[z] );
          for( i = 0 ; i < 5 ; i++ )
          {
           str2str( exp , formula[i] );

           strReplace( exp , "a" , num1 );
           strReplace( exp , "b" , num2 );
           strReplace( exp , "c" , num3 );
           strReplace( exp , "d" , num4 );
           strReplace( exp , "x" , op1 );
           strReplace( exp , "y" , op2 );
           strReplace( exp , "z" , op3 );

           evalExpression( exp , ret );
           if( fabs( ret - goal ) < .1e-4 )
           {
            //cout<<exp<<goal<<endl;
            str2str( expression[total++] , exp );
           }
          }
         }
        }
       }
      }
     }
    }
   }
  }
 }

 return true;
}


void main()
{
 double num[] = { 3 , 3 , 7 , 7 };
 int n = 4;
 int goal = 24;
 char exp[1024][lineMax];
 int total;
 int i;
 solve24( num , n , goal , exp , total );
 for( i = 0 ; i < total ; i++ )
  cout<<exp[i]<<endl;
}


程序分析:

当 num[] = { 3 , 3 , 7 , 7 } 时
输出结果为:
(3+(3/7))*7=
(3+(3/7))*7=
((3/7)+3)*7=
((3/7)+3)*7=
(3+(3/7))*7=
(3+(3/7))*7=
((3/7)+3)*7=
((3/7)+3)*7=
7*(3+(3/7))=
7*((3/7)+3)=
7*(3+(3/7))=
7*((3/7)+3)=
7*(3+(3/7))=
7*((3/7)+3)=
7*(3+(3/7))=
7*((3/7)+3)=

当 num[] = { 1 , 5 , 5 , 5 } 时
输出结果为:
(5-(1/5))*5=
(5-(1/5))*5=
5*(5-(1/5))=
5*(5-(1/5))=
(5-(1/5))*5=
(5-(1/5))*5=
5*(5-(1/5))=
5*(5-(1/5))=
(5-(1/5))*5=
(5-(1/5))*5=
5*(5-(1/5))=
5*(5-(1/5))=

当 num[] = { 4 , 5 , 6 , 7 } 时
输出结果为:
4*((5-6)+7)=
4*((5+7)-6)=
4*(5+(7-6))=
4*((7+5)-6)=
4*(7+(5-6))=
4*((7-6)+5)=
((5-6)+7)*4=
(5-(6-7))*4=
((5+7)-6)*4=
(5+(7-6))*4=
(5+7)*(6-4)=
(6-4)*(5+7)=
(6-4)*(7+5)=
((7+5)-6)*4=
(7+(5-6))*4=
(7+5)*(6-4)=
((7-6)+5)*4=
(7-(6-5))*4=

我们发现,程序并不能很还的解决多余括号问题,
如果有重复数出现时,程序也没有考虑结果式是否重叠。
这是本程序存在的缺点,
作者正在努力改进ing...


void char2str( char str[] , char ch )
void double2str( char str[] , double n )
bool evalExpression( char exp[] , double &ret )
void solve( double num1 , double num2 , char op , double &ret )
bool solve24( double num[] , int n , int goal , char expression[][lineMax] , int &total )
void str2str( char str[] , const char str2[] )
void strFind( const char str[] , const char subStr[] , int &reti , int &retj )
void strReplace( char str[] , const char subStr[] , char newStr[] )

以上是程序用到的函数,有些是比较实用的,也许在其它地方可能用到。

 

 

 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值