扑克牌24点
最近在vc6中练习静态库创建和调用,正好前段时间写过四则混合运算代码,就拿它来练手了。vc6创建静态库很简单,只要新建工程,选择“Win32 Static Library”即可,你的代码可纯粹只有你自己写的函数,不需要包含任何头文件;调用时将你生成的库文件放到新工程的目录下,在代码头部加“ #pragma comment(lib, 库文件名)”,再声明下你要调用的函数就可以使用库函数了。
24点扑克牌游戏是经典的益智类游戏,坛子里也有好多大神写过,我甚至看到有人纯粹用“if...else...”完成的。
游戏规则是:一副牌中抽去大小王剩下52张,任意抽取4张牌,用加、减、乘、除、括号把牌面上的数算成24。每张牌必须用一次且只能用一次,用11至13代表JQK。
仔细分析,算法应该很简单,就是暴力组合法:4个数字全排列为4!=24,3个运算符相当于3位4进制数共4*4*4=64,因此在不考虑括号的情况下应该有24*64=1536种,这用程序遍历应该瞬间可以完成。
1、现在分析加括号的情况,通过分析:由于括号只在乘除的情况下优先加减时使用,((a+b)*c)+d、((a+b)*c)*d和(a+b)*c+d、(a+b)*c*d等效,所以括号嵌套不予考虑。单层括号有(a+b)+c+d、(a+b+c)+d、a+(b+c)+d、a+(b+c+d)、a+b+(c+d)、(a+b)+(c+d)共6种情况(式子中的加好代表所有运算符),再加上不含括号共7种,这样一来,考虑括号时穷举所有组合共有7*24*64=10752种,这个数量电脑穷举起来也不费力吧。
2、本次使用了我自己写的静态库,我把代码和静态库以及编译成功后的exe一并打包了,该代码在vc6下编译成功,也尝试着在codeblock下编译,还不得要领,如果无法编译,可参考我在https://bbs.bccn.net/thread-485738-1-1.html一楼代码,拷贝过来即可不以来静态库了。我在静态库里的函数有点小改动,主要针对除不尽的情况,如8/3*9+6原代码也会算成24,我把静态库里除不尽的情况直接恒定返回123456(即8/3=123456),从而避开错误的正确结果,也规避了经典bug:8/(3-8/3)。
3、同上次一样,我在代码中留了两个问号,有兴趣的可以填空。
源文件和库文件
24点.rar
(31.82 KB)
运行效果
程序代码:
#include <stdio.h> #pragma comment(lib, "xzleval.lib") int eval(char *); int geteval(int *a,int k,int o) { char op[]="+-*/",kz[5]={0},kf[5]={0},ev[100]; int i,b[3]={0}; for(i=0;i<3;i++,o/=4)b[i]=o%4; //分解运算符组合(分解为3个4进制数组合) if((b[0]<2&&b[1]<2&&b[2]<2)||(b[0]>1&&b[1]>1&&b[2]>1))k=0; //运算符优先级相同不需要括号 //实际上括号只和乘除配对,可以在精简算式,太啰嗦就不再考虑怎么组织逻辑了 if(k) {//处理括号组合,0:无括号,6:两对括号,1-5:通过运算一对括号包含2个或3个操作数 if(k<6) {//一对括号 kz[(k-1)/2]='('; kf[(k+2)/2]=')'; } else {//两对括号 kz[0]=kz[2]='('; kf[1]=kf[3]=')'; } } //用sprintf组合算式 sprintf(ev,"%s%d%c%s%d%s%c%s%d%s%c%d%s",kz,a[0],op[b[0]],kz+1,a[1],kf+1,op[b[1]],kz+2,a[2],kf+2,op[b[2]],a[3],kf+3); if(eval(ev)==24) { printf("%s=24\n",ev); return 1; } return 0; } void per(int *a,int *b,int n) {//全排列 int i,j; if(n>=4) { for(i=0;i<7;i++) for(j=0;j<64;j++) b[4]+=geteval(b,i,j); //i对应括号组合,j对应运算符组合,总组合数为7*64*24=10752 } for(i=0;i<4;i++) { if(a[i]) { b[n]=a[i]; //? 填空 per(a,b,n+1); //? 填空 } } } void main() { int i,a[4],b[5]; while(1) { printf("输入4个1至13间的数(非数字退出):\n"); for(i=0;i<4;i++) { if(!scanf("%d",&a[i]))break; while(a[i]<1||a[i]>13) { printf("%d不合格,重新输入:",a[i]); if(!scanf("%d",&a[i]))break; } } if(i<4)break; printf("你输入的数分别是:"); for(i=0;i<4;i++)printf("%-3d",a[i]); printf("\n"); b[4]=0; per(a,b,0); if(b[4])printf("不考虑交换律结合律,共有%d个算式\n",b[4]); else printf("无法计算24点!\n"); } }