标题:扑克牌24点
只看楼主
xzlxzlxzl
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
来 自:湖北
等 级:贵宾
威 望:125
帖 子:1091
专家分:5825
注 册:2014-5-3
结帖率:100%
 问题点数:0 回复次数:13 
扑克牌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
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
得分:0 
回复 楼主 xzlxzlxzl
没有运行,但根据全排列函数思想,那个应该是交换两个数,递归完成后要复原,大体就是这样,(别问我怎么知道,全排列函数是有模板的)具体就不写了,还是留给有兴趣的来填(相信自己如果真的要填总是可以填出来的)~

另说最近自从写了个字符串排序算法后重点就不在算法上面了,而且那个算法还有很多可以完善的地方(总之最近接触了linux,有很多东西要弄)所以对于深一点的算法我一时也看不出什么来了,只能简单说说~

其实24点还可以用后缀表达式来算~
在全排列的情况下具体操作还有两个递归入口~
一个是数字进行递归,另一个是运算符进行递归~

例如


1 2 3 4 + - *这样进行递归可以有

1 2 3 4 + - *
1 2 3 + 4 - *
1 2 3 + - 4 *
1 2 + 3 4 - *
1 2 + 3 - 4 *

然后直接用后缀表达式进行运算(嗯这个应该比带括号的中缀表达式要简单一些,可以参考逆波兰计算器)

其实只是有5种情况,这种方法对于多于4个数也是有用的,楼主如果有兴趣拓展一下可以弄一种n个数的k点算法(感觉要是手动对括号进行匹配这个就会见笑了)~




[此贴子已经被作者于2018-5-6 23:33编辑过]


[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2018-05-06 23:27
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
得分:0 
还有楼主考虑除数为0的情况么(如果有就当我没有说)~

还有通过后缀表达式只是算出5种情况,而不是楼主所说的那7种,那5种对应的分别是

    a+(b+(c+d))
    a+((b+c)+d)
    (a+(b+c))+d
    (a+b)+(c+d)
    ((a+b)+c)+d

说透彻一点就是楼主只是利用了24点的运算优先级别只有两种这个空子,所以才会出现有1对括号的情况~

也就是说规定总是两个括号嵌套,也就是说这样就只是考虑括号运算问题而不是运算符优先级别问题了,这样实现就可以方便很多,对于n个数的状态而言,就不用手工去调和括号问题了

其实这还可以说一个很深入的问题,那就是两个表达式等价问题,怎么判断这两个表达式是否等价,并且将等价的归为一类,也就是说所有表达式都可以按照一个策略进行转化成唯一的规范表示方式,感觉要寻找这样一个策略要弄很多东西才行~


[此贴子已经被作者于2018-5-7 00:43编辑过]


[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2018-05-07 00:04
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
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
得分:0 
回复 4楼 xzlxzlxzl
稍微看了一下,的确不是交换,模板里面的就那么一个数组而不是两个,以前曾经自己写过一个全排列,也是没有按模板的,但理解角度不同而且个人感觉还比较复杂,从楼主的理解出发还要具体还要再看看才行~

当然n>4看了一下的确组合太多了,而且还有个问题就做乘法运算可能会出现数据溢出,没啥实用性,这个知道方法就可以了~

至于等价式那里比较复杂实现成本比较高,上网搜过一下范式这个概念,是属于数据库的,然后看到了说范式成本太高这也不是最佳数据库,有时候有些数据冗余也是好的--这样或者输出某些等价的重复方案可以大大降低实现成本~

所以简单实现24点就好了~



[此贴子已经被作者于2018-5-7 09:46编辑过]


[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2018-05-07 08:04
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
得分:0 
回复 4楼 xzlxzlxzl
那两个空我大概知道了,楼主其实已经给出一个明显的提示~

if  (a[i])

然而输入楼主已经限制只能是[1,13)整数区间里面的数了~
所以很明显是a[i]=0;和a[i]=b[n];

还有一个隐性一点的提示就是 if (a[i])这个是递归结束条件,因为除此之外再也没有其它递归出口了,所以这个排列只适用于原数据没有0的情况下(其实也可以改成INT_MAX这类之类不用到的数)

具体我没有时间去看是不是这样可以实现全排列,搜过全排列方法也不止一种,这种应该可以~

[此贴子已经被作者于2018-5-7 08:49编辑过]


[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2018-05-07 08:33
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
得分:0 
赠送楼主三段代码~

程序代码:
#include<stdio.h>

void per(int *a,int *b,int n);

int main( void )
{
    int a[4]={1,2,3,4};
    int b[4];
    
    per(a,b,0);
}

void per(int *a,int *b,int n)
{
    int i,j;
   
    if (n>=4)
    {
        size_t k;
        
        for (k=0;k<4;++k)
            printf("%-4d",b[k]);
            
        printf("\n");
    }
        
        
    for(i=0;i<4;i++)
        if (a[i])
        {
            b[n]=a[i];
            a[i]=0;
            per(a,b,n+1);
            a[i]=b[n];
        }
}


程序代码:
#include<stdio.h>

void per(int *a,int *b,int n);

int main( void )
{
    int a[4]={1,2,3,4};
    int b[4];
    
    per(a,b,0);
}

void per(int *a,int *b,int n)
{
    int i,j;
   
    if (n>=4)
    {
        size_t k;
        
        for (k=0;k<4;++k)
            printf("%-4d",b[k]);
            
        printf("\n");
    }
        
        
    for(i=n;i<4;i++)
    {
        b[n]=a[i];
        a[i]=a[n];
        per(a,b,n+1);
        a[i]=b[n];
    }
}




程序代码:
#include<stdio.h>

void per(int *a,int n);
void swap(int *a,int *b);

int main( void )
{
    int a[4]={1,2,3,4};
    
    per(a,0);
}

void swap(int *a,int *b)
{
    int t=*a;
   *a=*b;
   *b=t;
}

void per(int *a,int n)
{
    int i,j;
   
    if (n>=4)
    {
        size_t k;
        
        for (k=0;k<4;++k)
            printf("%-4d",a[k]);
            
        printf("\n");
    }
        
        
    for(i=n;i<4;i++)
        if (i==n)
            per(a,n+1);
        else
        {
            swap(&a[i],&a[n]);
            per(a,n+1);
            swap(&a[i],&a[n]);
        }
}


[此贴子已经被作者于2018-5-7 09:49编辑过]


[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2018-05-07 09:30
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
得分:0 
不过用vc我还有一个问题需要请教一下x版主~
就是新建一个工程后在工程里面加入了若干个.c文件,但现在其中一些没有用需要删掉,但直接删掉文件工程管理信息并没有改变,所以请教一下如果需要转移,或者删除其中某个.c文件那需要怎么弄?~

[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2018-05-07 14:21
xzlxzlxzl
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
来 自:湖北
等 级:贵宾
威 望:125
帖 子:1091
专家分:5825
注 册:2014-5-3
得分:0 
回复 8楼 九转星河
难道不是选择右边workspace-fileview-选中要移出工程的.c文件-直接按delete键?我都是这样操作的。
2018-05-07 21:42
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
得分:0 
回复 9楼 xzlxzlxzl
哇,我那个vc是中文版的,就是这样,搜了都是用英文,无语去了~

[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2018-05-07 23:33



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-486711-1-1.html




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

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