24点扑克牌游戏的算法实现

二十四点扑克牌游戏大概所有人都玩过,规则非常简单,随机抽出四张牌,由1到9中的数字组成(当然也可以扩展到任意整数),然后利用加减乘除以及括号组成一个算术表达式,计算这个表达式的结果是否能够为24(或任意整数)。看到这个题的第一反应就是利用穷举法来做,也就是建立一个搜索树,把所有的可能枚举出来,然后检查每种可能是否结果可以为24。基于这种思想,我们可以把问题分成三个步骤:

首先可以列出4个整数的所有排列,也就是求集合中元素的所有排列的问题,眼熟了吧?相信很多人都看过这个问题,一般的方式是用函数的递归来实现:如果用数组A[4]来保存这4个整数,则第0个位置的可能为4种,第1个位置的可能为3种(因为在前面已经确定1个元素),第2个位置的可能为2种,第3个位置的可能为1种,这样所有的可能恰好为P(4,4)=24。当然对于有重复元素的情况,比如{1,5,5,5}的全排列数为4,我们需要在递归函数中去掉重复,以减少不必要的计算。

其次我们要确定算术表达式中4个整数之间的3个运算符,运算符的确定和前面确定整数的方式类似,只不过我们是从4个运算符中选三次来确定,所以第1个运算符的可能是4种,第2个运算符的可能也是4种,第3个也是如此(因为每一次都可以选择4个运算符),根据算法原则,所有的可能为4*4*4=64种。如果所有的运算符的优先级都是一样的话,则这个问题也就到此便可以得出结果了,所有的可能是24*64=1536种。是不是很easy? OK,我们继续分析。

第三步我们来将运算符的优先级以及可能的括号加进去。优先级?括号?这两个东西比较麻烦,因为每个整数的前后都可能出现括号,括号中的内容具有高优先级;而运算符中的乘除的优先级高于加减,就算它们出现在后边也要先进行运算,怎么办呢?让我们来借鉴一下编译原理中的思想:普通的的算术表达式是中缀表达式,比如((1+2)+3)*4,要去掉这两个麻烦的东西,最好的途径是采用后缀表达式(逆波兰式, Reverse Polish Notation)来表示,例如前面那个算术表达式的逆波兰式形式为12+3+4*。这样就简单明了了吧?!这个步骤于是可以这样来做,对于确定的整数和运算符,找出所有可能的逆波兰式进行运算,如果结果为24,则将这个逆波兰式转为中缀形式进行输出(之所以这样做是中缀表达式更符合人们的阅读习惯,当然你也可以略过这一步)。现在思路已经很清晰了,那么剩下的工作就是如何实现的问题了。

解析逆波兰式的标准算法是利用stack来做,加减乘除都是二元运算符,也就是说运算前stack里的元素必须为2以上才合法,于是这个找出所有逆波兰式的问题就变成了一个附加条件下求所有可能的出栈序列。解析的递归函数可以这样来构建:结束的条件是所有的运算符都已经进行运算了,这时候所有的整数都已经运算过,stack里只有一个top位置的值,其便是最后的计算结果,可以直接将其和24进行比较;进行递归的路径有两条,一是检查stack里的元素是否大于或等于2个,如果是,则将它们pop出来,取出当前的运算符进行运算,把结果push回stack,然后递归,另一条是将当前的整数push进stack,直接递归。这样构建下的搜索树可以覆盖所有可能的出栈序列,也就是我们要的逆波兰式。

(代码由VC++2005/2008编译通过)

#include "stdafx.h"#include <assert.h>#include <iostream>#include <vector>#include <string>#include <utility>#include <stack>#include <queue>#include <algorithm>

#define double_equal(a, b) ((a-b<0.00001) && (b-a)<0.00001)#define OPSSIZE 4#define EXPRESULT 24std::vector<std::string> outputlist;char operators[OPSSIZE] = {'+','-','*','/'};

void antipolish(std::stack<double>& s, std::queue<int>& q, std::vector<int>& nums, std::vector<char>& ops){    if(ops.size() == 0 )    {        assert(nums.size()==0 && s.size()==1);        if(double_equal(s.top(), EXPRESULT))        {            std::string str;            while(!q.empty())            {                str += q.front();                q.pop();            }            outputlist.push_back(str);        }        return;    }    std::stack<double> temps = s;    std::queue<int> tempq = q;    std::vector<int> tempnums = nums;    std::vector<char> tempops = ops;    if(s.size() >= 2)    {        double operand2 = s.top();        s.pop();        double operand1 = s.top();        s.pop();

switch(ops.front())        {        case '+':            operand1 += operand2;            break;        case '-':            operand1 -= operand2;            break;        case '*':            operand1 *= operand2;            break;        case '/':            if(!double_equal(operand2, 0))                operand1 /= operand2;            else                return;            break;        default:            return;        }        s.push(operand1);        q.push(ops.front());        ops.erase(ops.begin());        antipolish(s, q, nums, ops);        s = temps;        q = tempq;        ops = tempops;    }    if(nums.size() >0)    {        s.push(nums.front());        q.push(nums.front()+0x30);        nums.erase(nums.begin());        antipolish(s, q, nums, ops);        s = temps;        q = tempq;        nums = tempnums;    }}

void search(std::vector<int> nums, int n, std::vector<char> ops){    if(n == (static_cast<int>(nums.size())-1))    {        std::stack<double> s;        std::queue<int> q;        antipolish(s, q, nums, ops);        return;    }    for(int i=n; i<static_cast<int>(nums.size()); i++)    {        bool duplicated = false;        for(int k=n; k<i; k++)            if(nums[i]==nums[k])            {                duplicated = true;                break;            }        if(!duplicated)        {            std::swap(nums[n], nums[i]);            for(int j=0; j<OPSSIZE; j++)            {                ops.push_back(operators[j]);                search(nums, n+1, ops);                ops.pop_back();            }            std::swap(nums[n], nums[i]);        }    }}

int _tmain(int argc, _TCHAR* argv[]){    std::vector<char> str;    std::vector<int> numbers;    numbers.push_back(1);    numbers.push_back(2);    numbers.push_back(3);    numbers.push_back(4);    search(numbers, 0, str);    return 0;}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索递归
, include
, 运算符
, 表达式
, 中缀表达式
, 整数
, 优先级
, 后序波兰数 栈
, 逆波兰式
, 24个
递归运算
扑克牌24点游戏、扑克牌24点游戏规则、扑克牌24点游戏题目、扑克牌24点游戏下载、扑克牌24点游戏技巧,以便于您获取更多的相关知识。

时间: 2024-10-06 12:22:04

24点扑克牌游戏的算法实现的相关文章

Java编写的24点纸牌游戏_java

任意4个1-13数字,加减乘除计算24点. 实现原理: 1)排列组合4个数字 2)计算每次排列组合的可能性 Cal24.java import java.util.HashSet; import java.util.Set; public class Cal24 { private static final double precision = 0.00001; private static final int target = 24; public String[] execute(Strin

赛车游戏相关算法请教

问题描述 赛车游戏相关算法请教 在赛车游戏中,远处的画面根据车速逐渐靠近的算法怎么写呢?就是根据车速画面由远至近的大小变化如何控制? 解决方案 我猜测是根据速度算出,图片显示的数序关系,完后投影映射?

JAVA collection集合之扑克牌游戏实例_java

Collection 层次结构中的根接口.Collection表示一组对象,这些对象也称为collection的元素.一些 collection 允许有重复的元素,而另一些则不允许.一些 collection 是有序的,而另一些则是无序的.JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现.此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection. 主要内容:这里使用collection集合,模拟香港电影中大佬们玩的

求两人玩的扑克牌游戏

问题描述 急急急!!!!求大神帮忙,用c#窗体运用程序写一个两人玩的纸牌游戏,就是我们常说的挑三张.简单的游戏规则就是1.给每人发三张牌,牌面向上2.最大的为三匹(同点)比如说2,2,2,其次是同花如核桃QKA,接下来是顺子花色不同的QKA.发完牌比较弹出窗口显示赢家(玩家用A,B表示)点数相同为平局 解决方案 解决方案二:http://download.csdn.net/detail/sharingall/4429115http://download.csdn.net/detail/hitaf

用Visual Basic.NET编写扑克牌游戏

visual 扑克游戏林林总总,同一种游戏各地玩法亦不尽相同.编程爱好者多喜欢编写一些本地玩法的扑克游戏.那么,编写自己的扑克游戏该从何处入手呢? 扑克游戏编程关键有两点:一是扑克牌面的绘制:二是扑克游戏规则的算法实现.初学扑克游戏编程的爱好者可从一些简单的游戏.借用一些现有资源开始.本文拟借用Windows自带的Cards.dll和简单的21点游戏为例,介绍扑克游戏编程的初步方法. 一. 扑克牌面绘制 Cards.dll支持Windows自带的游戏,如Solitaire(纸牌游戏).如果我们知

Flash小游戏-连连看算法大揭密

算法 个题目有些夸张了,因为这个游戏本来是个比较简单的游戏. 不过,这个简单游戏的技术问题是一类游戏如俄罗斯方块,纸牌游戏等制作的基础.通过对这个游戏算法的分析,特别是对检查连线的探讨,大家可以基本掌握这类游戏算法的基本思维方法. 连连看游戏规则很简单,就是点中两个互相匹配并且可以通过不多于两个折点的折线连在一起的方块后,这两个方块就可以消掉.所以,下图的三种情况可以把方块消掉.笔者的游戏中,配对规则是两数相加等于100. 配对的检查比较简单,只要用一个if语句,条件a+b==100就可以检验了

js+ajax实现的A*游戏路径算法整理第1/2页_javascript技巧

作者:llinzzi 日期:2007-05-24 去年做了个小东西,一个在线WebGame,目前只实现了多人聊天,移动,拖动画面移动,场景系统等,可以当场景聊天室使用.不过扔了一年了.如图 美工由静电设计后台将由老于开发 今年想再捡起来好好做做,由于是基于坐标点的,所以移动路径非常费资源.找到了一个A*的路径算法,挺智能. 转载一些介绍[转自 http://data.gameres.com/message.asp?TopicID=25439]译者序:很久以前就知道了A*算法,但是从未认真读过相关

Javascript和HTML5利用canvas构建Web五子棋游戏实现算法

这只是一个简单的JAVAscript和HTML5小程序,没有实现人机对战. 五子棋棋盘落子点对应的二维数组.数组的元素对应落子点.比如数组元素值为0表示该元素对应的落子点没有棋子,数组元素值为1表示该元素对应的落子点有白棋子,数组元素值为2表示该元素对应的落子点有黑棋子: 判断五子棋赢棋的算法是通过对五子棋棋盘落子点对应的二维数组的操作来实现的. 判断五子棋赢棋算法 下边的函数可以实现判断五子棋赢棋的算法,也可以按照教材中相应的算法实现. 其中函数的参数xx.yy为数组下标,chess数组实现五

javascript和HTML5利用canvas构建猜牌游戏实现算法

让我猜猜你心中的牌,先随机生成27张牌,不能重复列出三列牌,然后记住其中一张,然后点击牌所在的列,多次就可以猜出你想的牌,具体实现如下,感兴趣的朋友可以参考下哈   让我猜猜你心中的牌,先随机生成27张牌,不能重复列出三列牌,然后记住其中一张,然后点击牌所在的列,多次就可以猜出你想的牌. 如果是9张只要猜2次,如果是27张就是猜3次. 实现方法(27张): 如果点击了第三列,那就是说牌一定在这9张里面,就把第三列的9张牌平均给每列分3张,假设编号为123,456,789 再点击一次,如果点击第二