有一个c语言的计算24游戏的程序源代码~有些地方自己注释了~但整体看不懂~恳请哪位大侠解释下?(求注释)

作者&投稿:长亮 (若有异议请与网页底部的电邮联系)
适合初学者的24点游戏C语言源代码~

关于二十四点游戏的编程思路与基本算法

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

问题既然出现了,我们当然要解决。冥思苦想之际,我的脑中掠过一丝念头——何不编个程序来解决这个问题呢?文曲星中不就有这样的程序吗?所以这个想法应该是可行。想到这里我立刻开始思索这个程序的算法,最先想到的自然是穷举法(后来发现我再也想不到更好的方法了,悲哀呀,呵呵),因为在这学期我曾经写过一个小程序——计算有括号的简单表达式。只要我能编程实现四个数加上运算符号所构成的表达式的穷举,不就可以利用这个计算程序来完成这个计算二十四点的程序吗?确定了这个思路之后,我开始想这个问题的细节。
首先穷举的可行性问题。我把表达式如下分成三类——
1、 无括号的简单表达式。
2、 有一个括号的简单表达式。
3、 有两个括号的较复4、 杂表达式。
穷举的开始我对给出的四个数进行排列,其可能的种数为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--;
}
}
}

正如上面函数中提到的,在完成四张牌的排列之后,在表达式中添加运算符号。由于只有四张牌,所以只要添加三个运算符号就可以了。由于每一个运算符号可重复,所以计算出其可能的种数为4*4*4=64种。仍然利用嵌套函数实现添加运算符号的穷举,算法如下:

/* 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)
这个for循环给出了添加一个括号的可能性的种数,其中m、n分别为添加在表达式中的左右括号的位置。我所说的多余的是指m=0,n=8,也就是放在表达式的两端。这真是多此一举,呵呵!最后一种情况是添加两个括号,我分析了一下,发现只可能是这种形式才不会是重复的——(a b)(c d)。为什么不会出现嵌套括号的情况呢?因为如果是嵌套括号,那么外面的括号肯定是包含三个数字的(四个没有必要),也就是说这个括号里面包含了两个运算符号,而这两个运算符号是被另外一个括号隔开的。那么如果这两个运算符号是同一优先级的,则肯定可以通过一些转换去掉括号(你不妨举一些例子来试试),也就是说这一个括号没有必要;如果这两个运算符号不是同一优先级,也必然是这种形式((a+-b)*/c)。而*和/在这几个运算符号中优先级最高,自然就没有必要在它的外面添加括号了。

综上所述,所有可能的表达式的种数为24*64*(1+6+1)=12288种。哈哈,只有一万多种可能性(这其中还有重复),这对于电脑来说可是小case哟!所以,对于穷举的可行性分析和实现也就完成了。


接下来的问题就是如何对有符号的简单表达式进行处理。这是栈的一个著名应用,那么什么是栈呢?栈的概念是从日常生活中货物在货栈种的存取过程抽象出来的,即最后存放入栈的货物(堆在靠出口处)先被提取出去,符合“先进后出,后进先出”的原则。这种结构犹如子弹夹。
在栈中,元素的插入称为压入(push)或入栈,元素的删除称为弹出(pop)或退栈。

栈的基本运算有三种,其中包括入栈运算、退栈运算以及读栈顶元素,这些请参考相关数据结构资料。根据这些基本运算就可以用数组模拟出栈来。

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

第一种方法——
首先建立两个栈,操作数栈OVS和运算符栈OPS。其中,操作数栈用来记忆表达式中的操作数,其栈顶指针为topv,初始时为空,即topv=0;运算符栈用来记忆表达式中的运算符,其栈顶指针为topp,初始时,栈中只有一个表达式结束符,即topp=1,且OPS(1)=‘;’。此处的‘;’即表达式结束符。
然后自左至右的扫描待处理的表达式,并假设当前扫描到的符号为W,根据不同的符号W做如下不同的处理:
1、 若W为操作数
2、 则将W压入操作数栈OVS
3、 且继续扫描下一个字符
4、 若W为运算符
5、 则根据运算符的性质做相应的处理:
(1)、若运算符为左括号或者运算符的优先级大于运算符栈栈顶的运算符(即OPS(top)),则将运算符W压入运算符栈OPS,并继续扫描下一个字符。
(2)、若运算符W为表达式结束符‘;’且运算符栈栈顶的运算符也为表达式结束符(即OPS(topp)=’;’),则处理过程结束,此时,操作数栈栈顶元素(即OVS(topv))即为表达式的值。
(3)、若运算符W为右括号且运算符栈栈顶的运算符为左括号(即OPS(topp)=’(‘),则将左括号从运算符栈谈出,且继续扫描下一个符号。
(4)、若运算符的右不大于运算符栈栈顶的运算符(即OPS(topp)),则从操作数栈OVS中弹出两个操作数,设先后弹出的操作数为a、b,再从运算符栈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,所谓表达式的线性化是指将中缀表达的表达式转化为波兰表达式。对于每一个表达式,利用栈可以把表达式变换成波兰表达式,也可以利用栈来计算波兰表达式的值。

至于转换和计算的过程和第一种方法大同小异,这里就不再赘述了。

下面给出转换和计算的具体实现程序——

/* 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); }
}

这样这个程序基本上就算解决了,回过头来拿这个程序来算一算文章开始的那个问题。哈哈,算出来了,原来如此简单——(6-3)*10-6=24。

最后我总结了一下这其中容易出错的地方——

1、 排列的时候由于一个数只能出现一次, 所以必然有一个判断语句。但是用什么来判断,用大小显然不行,因为有可能这四个数中有两个或者以上的数是相同的。我的方法是给每一个数设置一个代号,在排列结束时,通过这个代号找到这个数。

2、在应用嵌套函数时,需仔细分析程序的执行过程,并对个别变量进行适当的调整(如j的值),程序才能正确的执行。

3、在分析括号问题的时候要认真仔细,不要错过任何一个可能的机会,也要尽量使程序变得简单一些。不过我的分析可能也有问题,还请高手指点。

4、在用函数对一个数组进行处理的时候,一定要注意如果这个数组还需要再应用,就必须将它先保存起来,否则会出错,而且是很严重的错误。

5、在处理用户输入的表达式时,由于一个十位数或者更高位数是被分解成各位数存放在数组中,所以需对它们进行处理,将它们转化成实际的整型变量。另外,在转化过程中,用一个字母来代替这个数,并将这个数存在一个数组中,且它在数组中的位置和代替它的这个字母有一定的联系,这样才能取回这个数。

6、由于在穷举过程难免会出现计算过程中有除以0的计算,所以我们必须对calc函数种对于除的运算加以处理,否则程序会因为出错而退出(Divide by 0)。

7、最后一个问题,本程序尚未解决。对于一些比较著名的题目,本程序无法解答。比如说5、5、5、1或者8、8、3、3。这是由于这些题目在计算的过程用到了小数,而本程序并没有考虑到小数。

C语言源程序是由:数据类型、常量与变量、数组、指针、字符串、文件输入/输出构成。
具体介绍:
1、数据类型
C的数据类型包括:整型、字符型、实型或浮点型(单精度和双精度)、枚举类型、数组类型、结构体类型、共用体类型、指针类型和空类型。
2、常量与变量
常量其值不可改变,符号常量名通常用大写。变量是以某标识符为名字,其值可以改变的量。标识符是以字母或下划线开头的一串由字母、数字或下划线构成的序列,请注意第一个字符必须为字母或下划线,否则为不合法的变量名。变量在编译时为其分配相应存储单元。
3、数组
如果一个变量名后面跟着一个有数字的中括号,这个声明就是数组声明。字符串也是一种数组。它们以ASCII的NULL作为数组的结束。要特别注意的是,方括内的索引值是从0算起的。
4、指针
指针不仅可以是变量的地址,还可以是数组、数组元素、函数的地址。通过指针作为形式参数可以在函数的调用过程得到一个以上的返回值,不同于return(z)这样的仅能得到一个返回值。
指针是一把双刃剑,许多操作可以通过指针自然的表达,但是不正确的或者过分的使用指针又会给程序带来大量潜在的错误。
5、字符串
C语言的字符串其实就是以'\0'字符结尾的char型数组,使用字符型并不需要引用库,但是使用字符串就需要C标准库里面的一些用于对字符串进行操作的函数。它们不同于字符数组。
6、文件输入/输出
在C语言中,输入和输出是经由标准库中的一组函数来实现的。在ANSI C中,这些函数被定义在头文件;中。

扩展资料:语言特点
1、高级语言:它是把高级语言的基本结构和语句与低级语言的实用性结合起来的工作单元。
2、结构式语言:结构式语言的显著特点是代码及数据的分隔化,即程序的各个部分除了必要的信息交流外彼此独立。这种结构化方式可使程序层次清晰,便于使用、维护以及调试。
3、代码级别的跨平台:由于标准的存在,使得几乎同样的C代码可用于多种操作系统,如Windows、DOS、UNIX等等;也适用于多种机型。C语言对编写需要进行硬件操作的场合,优于其它高级语言。
4、使用指针:可以直接进行靠近硬件的操作,但是C的指针操作不做保护,也给它带来了很多不安全的因素。C++在这方面做了改进,在保留了指针操作的同时又增强了安全性,受到了一些用户的支持。

先确认24点游戏规则如下:
“巧算24点”的游戏:一副牌中抽去大小王剩下52张,(如果初练也可只用1~10这40张牌)任意抽取4张牌(称牌组),用加、减、乘、除(可加括号)把牌面上的数算成24。每张牌必须用一次且只能用一次,如抽出的牌是3、8、8、9,那么算式为(9—8)×8×3或3×8+(9—8)或(9—8÷8)×3等。
(备注:有人统计过一副牌(52张)中,任意抽取4张可有1820种不同组合,其中有458个牌组算不出24点。)

然后分析代码部分:
int main(void) //主函数部分
{
search24(0); // 见chk函数,只要找到求解,用exit(0)直接终止程序
printf("No answer.\n"); // 表示无解情况
}

int search24(int d) //算法,整个程序的核心,注意d是递归结束条件
{
// 这里应该是楼主不明白的地方
// 建议用一组(3、8、8、9)实例跟踪调试,以理解别人的算法
// 这类问题需要用到算术表达式穷举法的知识,可以先学习这方面知识便于理解程序
}

其它三函数是进一步对数据处理
void make(int i, float p, float q, char o, int d)
float calc(float n1, float n2, char o)
void chk(float k)

简单介绍下算术表达式穷举法的知识和网络上以实现的算法原理:
基本原理是穷举4个整数所有可能的表达式,然后对表达式求值。
表达式的定义: expression = (expression|number) operator (expression|number)
因为能使用的4种运算符 + - * / 都是2元运算符,所以本文中只考虑2元运算符。2元运算符接收两个参数,输出计算结果,输出的结果参与后续的计算。
由上所述,构造所有可能的表达式的算法如下:
(1) 将4个整数放入数组中
(2) 在数组中取两个数字的排列,共有 P(4,2) 种排列。对每一个排列,
(2.1) 对 + - * / 每一个运算符,
(2.1.1) 根据此排列的两个数字和运算符,计算结果
(2.1.2) 改表数组:将此排列的两个数字从数组中去除掉,将 2.1.1 计算的结果放入数组中
(2.1.3) 对新的数组,重复步骤 2
(2.1.4) 恢复数组:将此排列的两个数字加入数组中,将 2.1.1 计算的结果从数组中去除掉
可见这是一个递归过程。步骤 2 就是递归函数。当数组中只剩下一个数字的时候,这就是表达式的最终结果,此时递归结束。
在程序中,一定要注意递归的现场保护和恢复,也就是递归调用之前与之后,现场状态应该保持一致。在上述算法中,递归现场就是指数组,2.1.2 改变数组以进行下一层递归调用,2.1.3 则恢复数组,以确保当前递归调用获得下一个正确的排列。
括号 () 的作用只是改变运算符的优先级,也就是运算符的计算顺序。所以在以上算法中,无需考虑括号。括号只是在输出时需加以考虑


C语言中怎么计算一个数的平方?
可以用b=a*a来算,也可以用math库的pow函数来算 要用pow首先要包含头文件 #include <math.h> 然后调用pow函数 double pow( double base, double exp );函数返回以base为底的exp次,不允许的取值范围:当base 为 0 且exp 小于或等于 0 当base 为 负数 且 exp 不为整数 ...

C语言如何输入一个两个整数运算的计算表达式,输出该表达式和结果,如...
用两个整形变量a,b,使用库函数printf 用两次,两个变量值分别赋2和3即可。

如何用C语言编程一个计算圆的面积的程序?
int iR;cin >> iR;cout << "半径为" << iR << "的圆面积为:" << 3.14*iR*iR << endl;

利用(一个)c语言计算1+2+3...+10+1*2*3...*10的值,求大神解答
include<stdio.h>#define N 10void main() { int i,s,t; s=0; t=1; for ( i=1;i<=N;i++ ) { t*=i; s+=i; } \/\/ s将i的值累加,循环完成后s=1+2+3+..+10 \/\/ t将i的值累乘,循环完成后t=1*2*3*...*10 s+=t; printf("%d\\n",s);} ...

用c语言编写一个简单计算器程序
#include<stdio.h>//计算器 voidmenu()//自定义的菜单界面 { printf("---\n");printf("请输入你的选择\n");printf("1.+\n");printf("2.-\n");printf("3.*\n");printf("4./\n");printf("---\n");} intmain(){ int...

一个C语言问题..输入两个整数a和n,计算下面表达式的值。
根据题目给出的表达式,可以得到一个递推式:sum = a + aa + aaa + …… + aa...a(共n个a)其中,aa...a表示a重复n次。为了方便计算,我们可以将每一项拆开来单独计算,然后将它们相加。具体来说,对于第i项,它的值为 ai = a*10^(i-1) * (1+10+(10^2)+...+(10^(n-i)...

一个c语言的题目:
我来 很快 12345 您输入的数万位是:1,千位是:2,百位是:3,十位是:4,个位是:5。Press any key to continue include "stdio.h"main(){ int num,a,b,c,d,e; \/\/定义输入数字,各个位的参数 scanf("%d",&num); \/\/输入数字 a=num\/10000; \/\/万位 b=num%10000\/1000;...

编写一个c语言程序,计算表达式:S=2\/1+3\/2+…+(n+1)\/n
include<stdio.h>int main(){int i,n; double s=0; scanf("%d",&n); for(i=1;i<=n;i++) s+=1+1.0\/i; printf("%lf\\n",s); return 0;}

实现一个简单的计算程序,计算两个20位大整数的和(必须用简单C语言实 ...
这个是两个大数相加,double类型会丢失精度。我的程序可以任意位数的数相加,只要把数组改大点就行了。如果有什么不懂得可以加百度Hi好友问我,很乐意为你解答。include<stdio.h> include<string.h> void main(){ char a[25],b[25];int key1[25],key2[25],answer[25];int len1,len2,t;sc...

c语言从键盘输入若干个整数,输入-2表示结束,求这些整数的平均数
下面是一个简单的C语言程序,它从键盘输入若干个整数,直到输入-2为止,并计算这些整数的平均数:这个程序使用一个循环来反复读取整数,直到输入-2为止。在循环中,将每次输入的整数累加到sum中,并递增count以记录输入的整数个数。最后,计算平均值并输出结果。程序会防止除数为零的情况,如果没有输入...

广昌县18383604254: 24点游戏用c语言怎么写?? -
塔备香砂: 你首先要懂得C语言编写规则,其次了解24点游戏的玩法及规则,然后就可以选择一个合适的编译器,开始编写24点游戏了

广昌县18383604254: 如何用C语言做一个24点游戏的程序? -
塔备香砂: /*6.3.4 源程序*/ #define N 20 #define COL 100 #define ROW 40 #include "stdio.h" #include "time.h" /*系统时间函数*/ #include "graphics.h" /*图形函数*/ #include "alloc.h"/*动态地址分配函数*/ #include "stdlib.h" /*库函数*/ #include "...

广昌县18383604254: C语言实现加减乘除24游戏 -
塔备香砂: int d); double figure(char *p); void draw(int *p); default: printf("Please input 1;,&c[i]);stdio,int d) { char *change(int x); double figure(char *p); int i[4],n,n1,n2,n3,n4,s1,s2,s3,t,flag=0; char s[4][2]={"+","-","*","/"},p[64]={0},p1[64]={0}; i[0]=a; i[1]=b; i[2]=c; i[3]=d; for(n1=0;n1

广昌县18383604254: 求个C语言的24点游戏源代码
塔备香砂: 用if 或者switch 就可以实现.下面分别用两种语句写的: 用if: #include "stdio.h" void main() { char a; scanf("%c",&a); if (a=='Y'||a=='y') printf("1\n"); if(a=='N'||a=='n') printf("0\n"); } 用switch :(请输大写) #include "stdio.h" void main(...

广昌县18383604254: 加减乘除求24点的游戏怎么用C语言编啊? -
塔备香砂: #include "stdio.h" #include "math.h" #include "time.h" void main() { void first(); void second(); int third(); time_t t; int close=0; char get; clrscr(); srand((unsigned) time(&t)); for(;;) { clrscr(); printf("24 POINTS SYSTEM v2.2\n"); printf("MADE ...

广昌县18383604254: 求助怎么用C语言设计一个算24游戏? -
塔备香砂: #include"math.h" int total=0,count=0,page; char temp; main(){float sum(float,float,char);void test24(float [],char []);int i,j,k,l,q,r,s,t,n=0;float a[4];char f[4]={'+','-','*','/'};float x[4];char y[3];int go=1;while(go==1){printf("Please input how many ...

广昌县18383604254: 适合初学者的24点游戏C语言源代码 -
塔备香砂: 关于二十四点游戏的编程思路与基本算法 漫长的假期对于我来说总是枯燥无味的,闲来无聊便和同学玩起童年时经常玩的二十四点牌游戏来.此游戏说来简单,就是利用加减乘除以及括号将给出的四张牌组成一个值为24的表达式.但是其中却...

广昌县18383604254: C语言程序设计“算24”游戏求救! -
塔备香砂: #include<iostream>#include<math.h> using namespace std; const double MIN=1E-6; void Print(int *Rank,double *FourNum) { for(int i=0;i<4;i++) cout<<FourNum[Rank[i]]<<" "; cout<<endl; } void Calculate_24(int *Rank,double *FourNum,char *...

广昌县18383604254: C语言24点游戏 -
塔备香砂: #include <stdio.h>#include <math.h> double num[4];//存储4个数字 double pre = 1E-6; //精度 int go(int n) { int i,j; if(n==1) { if(fabs(num[0]-24)<1E-6) return 1; else return 0; } else { for(i=0;i<n-1;i++) { double a=num[i]; double b=num[i+1]; for(j=i+1;j<n-...

广昌县18383604254: 加减乘除求24点的游戏怎么用C语言编啊?
塔备香砂: #includenbsp;“stdio.h“nbsp;#includenbsp;“math.h“nbsp;#includenbsp;“time.h“nbsp;voidnbsp;main()nbsp;{nbsp;nbsp;nbsp;nbsp;voidnbsp;first();nbsp;nbsp;nbsp;nbsp;voidnbsp;second();nbsp;nbsp;nbsp;intnbsp;third();nbsp;nbsp;nbsp;...

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网