关于二十四点游戏的编程思路与基本算法 2003-4-10 7:55:13 VCOK 檀银兵

-- Zoom.Quiet [2004-08-12 16:44:57]

1. 问题

漫长的假期对于我来说总是枯燥无味的,闲来无聊便和同学玩起童年时经常玩的二十四点牌游戏来。此游戏说来简单,就是利用加减乘除以及括号将给出的四张牌组成一个值为24的表达式。但是其中却不乏一些有趣的题目,这不,我们刚玩了一会儿,便遇到了一个难题——3、6、6、10(其实后来想想,这也不算是个太难的题,只是当时我们的脑筋都没有转弯而已,呵呵)。

= 解决问题 ==

首先穷举的可行性问题。我把表达式如下分成三类——

穷举的开始我对给出的四个数进行排列,其可能的种数为4*3*2*1=24。我利用一个嵌套函数实现四个数的排列,算法如下:

/* ans[] 用来存放各种排列组合的数组 */
/* c[] 存放四张牌的数组 */
/* k[] c[]种四张牌的代号,其中k[I]=I+1。
用它来代替c[]做处理,考虑到c[]中有可能出现相同数的情况 */
/* kans[] 暂存生成的排列组合 */
/* j 嵌套循环的次数 */

int fans(c,k,ans,kans,j)
int j,k[],c[];char ans[],kans[];
{ int i,p,q,r,h,flag,s[4],t[4][4];
for(p=0,q=0;p<4;p++)
{ for(r=0,flag=0;r if(k[p]!=kans[r]) flag++;
if(flag==j) t[j][q++]=k[p];
}
for(s[j]=0;s[j]<4-j;s[j]++)
{ kans[j]=t[j][s[j]];
if(j==3) { for(h=0;h<4;h++)
ans[2*h]=c[kans[h]-1]; /* 调整生成的排列组合在最终的表
达式中的位置 */
for(h=0;h<3;h++)
symbol(ans,h); /* 在表达式中添加运算符号 */
}
else { j++;
fans(c,k,ans,kans,j);
j--;
}
}
}
/* ans[],j同上。sy[]存放四个运算符号。h为表达式形式。*/
int sans(ans,sy,j,h)
char ans[],sy[];int j,h;
{ int i,p,k[3],m,n; char ktans[20];
for(k[j]=0;k[j]<4;k[j]++)
{ ans[2*j+1]=sy[k[j]]; /* 刚才的四个数分别存放在0、2、4、6位
这里的三个运算符号分别存放在1、3、5位*/ 
if(j==2)
{ ans[5]=sy[k[j]];
/* 此处根据不同的表达式形式再进行相应的处理 */
}
else { j++; sans(ans,sy,j--,h); }
}
}
for(m=0;m<=4;m+=2)
for(n=m+4;n<=8;n+=2)

在栈中,元素的插入称为压入(push)或入栈,元素的删除称为弹出(pop)或退栈。

1.1. 表达式的计算

1.1.1. 第一种方法

——

1.1.2. 第二种方法

——

表达式 波兰表达式

A-B AB-
(A-B)*C+D AB-C*D+
A*(B+C/D)-E*F ABCD/+*EF*-
(B+C)/(A-D) BC+AD-/
/* first函数给出各个运算符的优先级,其中=为表达式结束符 */
int first(char c)
{ int p;
switch(c)
{ case '*': p=2; break;
case '/': p=2; break;
case '+': p=1; break;
case '-': p=1; break;
case '(': p=0; break;
case '=': p=-1; break;
}
return(p);
}
/* 此函数实现中缀到后缀的转换 */
/* M的值宏定义为20 */
/* sp[]为表达式数组 */
int mid_last()
{ int i=0,j=0; char c,sm[M];
c=s[0]; sm[0]='='; top=0;
while(c!='\0')
{ if(islower(c)) sp[j++]=c;
else switch(c)
{ case '+':
case '-':
case '*':
case '/': while(first(c)<=first(sm[top]))
sp[j++]=sm[top--];
sm[++top]=c; break;
case '(': sm[++top]=c; break;
case ')': while(sm[top]!='(')
sp[j++]=sm[top--];
top--; break;
default :return(1);
}
c=s[++i];
}
while(top>0) sp[j++]=sm[top--];
sp[j]='\0'; return(0);
}
/* 由后缀表达式来计算表达式的值 */
int calc()
{ int i=0,sm[M],tr; char c;
c=sp[0]; top=-1;
while(c!='\0')
{ if(islower(c)) sm[++top]=ver[c-'a'];/*在转换过程中用abcd等来代替数,
这样才可以更方便的处理非一位数, 
ver数组中存放着这些字母所代替的数*/
else switch(c)
{ case '+': tr=sm[top--]; sm[top]+=tr; break;
case '-': tr=sm[top--]; sm[top]-=tr; break;
case '*': tr=sm[top--]; sm[top]*=tr; break;
case '/': tr=sm[top--];sm[top]/=tr;break;
default : return(1);
}
c=sp[++i];
}
if(top>0) return(1);
else { result=sm[top]; return(0); }
}

2. 总结

3. 作者:檀银兵

邮编:210016 联系地址:江苏省南京市御道街29号112信箱 电子邮件:[MAILTO] [email protected] 个人主页:[WWW] http://wcfall.126.com [QQ]9403509(欢迎编程爱好者加我为好友)

last edited 2004-08-12 16:44:57 by Zoom.Quiet