| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 4270 人关注过本帖, 2 人收藏
标题:扑克牌24点
取消只看楼主 加入收藏
xzlxzlxzl
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
来 自:湖北
等 级:贵宾
威 望:125
帖 子:1091
专家分:5825
注 册:2014-5-3
结帖率:100%
收藏(2)
 问题点数:0 回复次数:3 
扑克牌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");
    }
}
搜索更多相关主题的帖子: 代码 括号 组合 int printf 
2018-05-06 22:36
xzlxzlxzl
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
来 自:湖北
等 级:贵宾
威 望:125
帖 子:1091
专家分:5825
注 册:2014-5-3
收藏
得分:0 
回复 2楼 九转星河
那两个空不是数据交换,再说数据交换两行代码写的下?我的代码可能算法一般,但绝对都是独立思考的,一般原创性比较高,不会照搬什么模板。
关于后缀、逆波兰表达式,由于和实际习惯相差甚远,我没怎么了解。n个数k点如果不含括号就太简单了,穷举数目是n!*4^(n-1),如果含括号穷举数量就太庞大了,n>2就必须考虑单层括号,n>3就必须考虑括号嵌套,算法我想到一个,由于没有什么实用性,就没有去实现(需要的代码量不大)。
考虑四则混合运算的交换律和结合律,过滤等价的算式,目前算法我还没想好,似乎要开辟空间储存出现过的算式再扫描解决。

感谢版主指导!
2018-05-07 07:04
xzlxzlxzl
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
来 自:湖北
等 级:贵宾
威 望:125
帖 子:1091
专家分:5825
注 册:2014-5-3
收藏
得分:0 
回复 8楼 九转星河
难道不是选择右边workspace-fileview-选中要移出工程的.c文件-直接按delete键?我都是这样操作的。
2018-05-07 21:42
xzlxzlxzl
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
来 自:湖北
等 级:贵宾
威 望:125
帖 子:1091
专家分:5825
注 册:2014-5-3
收藏
得分:0 
动态库和静态库的共同点是用函数调用方式使用第三方代码,不同点在于代码编译成可执行的exe文件之后,静态库的内容会原样拷贝到exe文件里和自己写的函数融为一体,因此复制这个exe文件可以拿到其他机器上单独执行,而使用了动态库则只在exe文件头部有一个加载动态库文件接口,exe文件每次执行时都会动态加载动态库文件到内存才能正常运行,如果要拷贝到其他电脑上,则必须也要附带拷贝动态库文件,exe不能单独执行。
动态库比较灵活,有共享特性(多个exe同时使用一个时只需加载一次),加载执行速度较慢;静态库独立性强,相对好使用管理(你只要拷贝一个exe就可以拿到其它机器上了),加载执行速度快。
一楼代码geteval函数需要做写调整,那个使用运算符优先级相同就免除括号算法的有部分错误,当存在除法是“a/b*c*d”和“a/(b*c)*d”显然不一样,因此优先级相同的不能一概而论,修改后的代码如下,可显著减少算式冗余:
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(k)
    {//处理括号组合,0:无括号,6:两对括号,1-5:通过运算一对括号包含2个或3个操作数
        if((b[0]<2&&b[1]<2&&b[2]<2)||(b[0]==2&&b[1]==2))return 0;  //运算只有加减或连续两个乘法的不需要括号
        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;
}
感谢版主提供的几个全排列代码,看来都差不多,看来是不是要考虑一个非递归的全排列了。
我更需要一个能顺序返回全排列结果的函数,就是第一次调用返回1234,第二次调用返回1243……一直到返回0就是全排列完成。
2018-05-08 07:34
快速回复:扑克牌24点
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.016841 second(s), 10 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved