计算 24 点的 C++ 程序
24点是一种广为流传的益智游戏。本文将分享我大一C++课的一个选做作业:编写24点程序。
1.题目要求
从一副扑克牌(不含大小王)中选取四张作为输入(A代表1,J代表11,Q代表12,K代表13),每张牌用一次,进行加或减或乘或除四则运算,以及括号的使用和顺序的调整,使含有这四个数的表达式的结果等于24.如果能等于24,输出一种方法就可以;如果不可以,输出相应信息。
比如:输入 A 2 3 4
输出:((A*2)*3)*4=24
输入 A A A A
输出:不能算出24
注:本题中并未要求中间过程必须是整数,因此中间出现小数、分数也是可以的。考虑到计算机的截断误差,计算结果跟24相差小于1e-5即可认为等于24。
2.编程思路
要让四个数a, b, c, d,通过一定的顺序,进行一定的计算,可以穷举所有的顺序和计算方法,看有没有等于24的。
首先,需要对这四个数排列出所有的顺序。然后,按照一定的次序进行计算。其中,这四个数的组合次序分为两种类型:
第一种,从四个数中选两个数计算得到中间结果1;中间结果1再与剩余两数中的一个计算,得到中间结果2;中间结果2再和最后一个数计算得到最终结果。比如((4*6)+2)-2即为第一种。
第二种,从四个数中选两个数计算得到中间结果1,另两个数计算得到中间结果2,再将中间结果1和中间结果2得到最终结果。比如(4*6)+(2-2)即为第二种。
而两个数之间的计算,又分为6种。a+b,a-b,a*b,a/b自不必说,还有b-a,b/a两种。
这样即可覆盖四个数进行四则运算所有的表达式。
3.代码及分析
首先是函数的定义和全局变量的设置。具体函数的意义和代码将会在后面具体说明。
#include<iostream>
#include<string>
#include<cmath>
using namespace std;
//24points 2.0
// 作者:小高
//修改日期:2021-11-7
//修改内容:判断是否等于24时设置了不超过1e-5的误差限,以防在除法过程中损失精度
void input();//将输入的字符串存储到字符串类数组str[]中,再转化为对应的整数num[]
double cal(double,double,int s);//四则运算,s表示符号
bool int_right(int x[],int i);//判断该数组下标为i的元素是否与前面元素相同,无相同则为true,有相同则为false
void point24(int n);//计算24点的第一种情况,如((a+b)*c)/d
void point24_2();//计算24点的第二种情况,如(a+b)*(c+d)
void output(int a);//根据第a种情况以及数组newnum[]和sig[]的内容输出
string str[4];//输入的字符串
int num[4],newnum[4], sig[3];
//num[]是把字符串转换成数字之后的数组;newnum[]存储不同顺序的数字;
//sig[]表示数字之间的符号
double res[4];//中间结果
bool success;
首先是最基本的四则运算函数。
//四则运算,s表示符号
double cal(double x,double y,int s)
{
double result=0;
switch(s)
{
case 0:result=x+y;break;
case 1:result=x-y;break;
case 2:result=x*y;break;
case 3:result=x/y;break;
case 4:result=y-x;break;
case 5:result=y/x;break;
}
return result;
}
然后是void point24(int n),这个是采用第一种组合次序,尝试各种四则运算符计算24点。具体:在事先排好序的全局数组newnum[]中,先将newnum[3]作为result[3],与newnum[2]计算得result[2],再将result[2]与newnum[1]计算得result[1],最后将result[1]与newnum[0]计算得result[0]。
看result[0]是否等于24,如果等于,直接置全局变量success为true,函数直接返回;否则,更改运算符继续运算。
为了遍历所有的运算符,函数采用递归的方法。输入参数n表示数的个数,在主函数中肯定有n=4,而在该函数内部则会调用point24(n-1)。对于n=1的情况,改变result[1]和newnum[0]之间的运算符进行各种运算,如果返回后result[0]均不得24,则在函数point24(2)中,改变result[2]与newnum[1]之间的运算符,重新算出result[1]后再次执行point24(1),以此类推。具体如下。
void point24(int n)//计算24点的第一种情况
{
res[3]=newnum[3];
int i;
if(n==1)
for(i=0;i<=5;i++)
{
res[0]=cal(res[1],double(newnum[0]),i);
sig[0]=i;
if(fabs(res[0]-24.0)<=1e-5)
{
success=true;
return;
}
}
else
for(i=0;i<=5;i++)
{
res[n-1]=cal(res[n],double(newnum[n-1]),i);
sig[n-1]=i;
point24(n-1);
if(success==true)break;
}
}
下面是用第二种组合次序算24点的代码,对于给定顺序的数组,先把newnum[0]和newnum[1]进行运算得结果res[0],再将newnum[2]和newnum[3]进行运算得res[1],最后将res[1]和res[0]进行运算得res[2]。直接在for循环中遍历所有运算符并看res[2]是否为24.
void point24_2()//计算24点的第二种情况,其中sig[]不涉及到cal()中的case4和5
{
for(sig[0]=0;sig[0]<=3;sig[0]++)
{
res[0]=cal(double(newnum[0]),double(newnum[1]),sig[0]);
for(sig[2]=0;sig[2]<=3;sig[2]++)
{
res[1]=cal(double(newnum[2]),double(newnum[3]),sig[2]);
for(sig[1]=0;sig[1]<=3;sig[1]++)
{
res[2]=cal(res[0],res[1],sig[1]);
if(fabs(res[2]-24.0)<=1e-5)
{
success=true;
return;
}
}
}
}
}
还剩下四个数的排列次序问题没有解决。这个排列主要在主函数里解决。为此,设置了一个数组int B[4],用于存放0,1,2,3四个数,用回溯法形成各种排列,并让输入数据在newnum[]里也按B[4]的顺序排列。排好列后,先后执行上述两个函数,看是否成功算出24(即sucdcess==true)。
int main()//采用回溯法列出每一个排列。然后对每个排列计算24点,成功则结束。
{
input();
int i=0,n;
int B[4]={0,1,2,3};
//B[]是0,1,2,3四个数的排列
while(i>=0&&success==false)
{
if(int_right(B,i)==true)
{
while(i<3)
{
i++;
if(int_right(B,i)==false)break;
}
if(i==3&&int_right(B,i)==true)
{//下面将num[]的值变换顺序后赋给newnum[],然后运用两种方法算24点
for(n=0;n<4;n++)newnum[n]=num[B[n]];
point24(3);
if(success==true)output(1);
else
{
point24_2();
if(success==true)output(2);
}
}
}
while(B[i]==3&&i>=0)
{
B[i]=0;//置0
i--;//回溯
}
if(i>=0)B[i]++;//该位的值加一
}
if(success==false)cout<<"不能算出24"<<endl;
return 0;
}
其中调用int_right()是确保B[]排列中元素不重复。
//判断该数组下标为i的元素是否与前面元素相同,无相同则为true,有相同则为false
bool int_right(int x[],int i)
{
int j=0;
while(j<i)
{
if(x[j]==x[i])return false;
j++;
}
return true;
}
最后是输入和输出函数。输入函数将字符转换为数字,并在用户输入不合规的情况下给出提示。输出函数则会按第一种或者第二种情况分别输出表达式字符串。
void input()//将输入的字符串存储到字符串类数组str[]中,再转化为对应的整数num[]
{
cout<<"请输入A、2到10、J、Q、K中的四个,计算24点。(A代表1,J代表11,Q代表12,K代表13)"<<endl;
int i;
bool input_right;
for(i=0;i<4;i++)
{
do
{
input_right=true;
cin>>str[i];
if(str[i]=="A"||str[i]=="a")num[i]=1;
else if(str[i]=="10")num[i]=10;
else if(str[i]=="J"||str[i]=="j")num[i]=11;
else if(str[i]=="Q"||str[i]=="q")num[i]=12;
else if(str[i]=="K"||str[i]=="k")num[i]=13;
else if(str[i].size()==1&&str[i][0]>='2'&&str[i][0]<='9')
num[i]=str[i][0]-48;
else
{
cout<<"第"<<i+1<<"个输入错误,请重输!"<<endl;
input_right=false;
}
}while(input_right==false);
}
}
void output(int a)//根据第a种情况以及数组newnum[]和sig[]的内容输出
{
const char ch[6]={'+','-','*','/','-','/'};
const string outstr[14]={"\0","A","2","3","4","5","6","7","8","9","10","J","Q","K"};
if(a==1)
{
string ansstr=outstr[newnum[3]];
int n;
for(n=2;n>=0;n--)
{
if(sig[n]>=0&&sig[n]<=3)ansstr=ansstr+ch[sig[n]]+outstr[newnum[n]];
else ansstr=outstr[newnum[n]]+ch[sig[n]]+ansstr;
if(n>0)ansstr='('+ansstr+')';
}
cout<<ansstr<<"=24"<<endl;
}
if(a==2)
{
cout<<'('<<outstr[newnum[0]]<<ch[sig[0]]<<outstr[newnum[1]]<<')'<<ch[sig[1]];
cout<<'('<<outstr[newnum[2]]<<ch[sig[2]]<<outstr[newnum[3]]<<')'<<"=24"<<endl;
}
}
4.运行测试
>> 请输入A、2到10、J、Q、K中的四个,计算24点。(A代表1,J代表11,Q代表12,K代表13)
<< 9 6 8 5
>> (9+6)*(8/5)=24
>> 请输入A、2到10、J、Q、K中的四个,计算24点。(A代表1,J代表11,Q代表12,K代表13)
<< 2 0 3 4
>> 第2个输入错误,请重输!
<< A
>> ((A*4)*3)*2=24
>> 请输入A、2到10、J、Q、K中的四个,计算24点。(A代表1,J代表11,Q代表12,K代表13)
<< 7 4 4 7
>> (4-(4/7))*7=24
>> 请输入A、2到10、J、Q、K中的四个,计算24点。(A代表1,J代表11,Q代表12,K代表13)
<< K K K K
>> 不能算出24
如果程序运行或者文章内容有误,欢迎在评论区指出。
上一篇: 24 点计算器问题 [C++ 实现]
推荐阅读
-
ARP 协议有什么作用--ARP(AddressResolutionProtocol,地址解析协议)地址解析协议用于将计算机的网络地址(32 位 IP 地址)转换为物理地址(48 位 MAC 地址)[RFC826]。ARP 协议属于链路层协议,在以太网网络中,数据帧从一台主机传送到网络中的另一台主机时,是根据 48 位以太网地址(硬件地址)来确定接口的,而不是根据 32 位 IP 地址。内核(如驱动程序)必须知道目的地的硬件地址,才能发送数据。当然,点对点连接不需要 ARP 协议。 ARP 协议示例
-
哪款应用程序收取家庭电费最便宜?哪款应用程序充家庭电费最便宜?充电电费 95 折是如何计算的?
-
C 语言程序设计--零起点入门指南(第 3 版) I 1.2 编写 C 程序的必要条件
-
60岁拿2000元创业,靠 "神 "做生意,如今年赚24亿--如今的天堂伞集团,无论是口碑还是销量,都是业内首屈一指的 "大牛"。王斌章的一把伞,仅去年的销售额就实现了近6亿元的好成绩。雨伞销售更是占据了中国所有雨伞行业的最大份额。可以说,如今的天堂伞,无论是质量还是口碑,都堪称行业翘楚。 一把天堂伞,如何在王斌章手中打出新高度,玩出大生意?总结起来,两点制胜法宝:一是质量绝对保证,二是服务有保障。这看似很简单,但几十年来不折不扣地执行,特别是在保护伞这个不太重要的对象上,想要做到始终如一,难度很大。这也是为什么中国只有一个王斌章被称为 "全球伞王 "的原因。 天堂伞成立之初,销售场地选在附近的一个广场,以摆摊的形式销售王斌章亲手制作的杭州伞。凭借几十年的手艺和严谨的态度,加上上乘的伞料,即使价格比普通伞高3倍5倍,也打开了市场,积累了第一批人气和资金。
-
35 岁实现财务*,腾讯程序员手握2300万提前退休?-1000万房产、1000万腾讯股票、加上300万的现金,一共2300万的财产。有网友算了一笔账,假设1000万的房产用于自住,剩下1300万资产按照平均税后20-50万不等进行计算,大约花上26-60年左右的时间才能赚到这笔钱。也就是说,普通人可能奋斗一辈子,才能赚到这笔钱。在很多人还在为中年危机而惶惶不可终日的时候,有的人的35岁,就已经安全着陆,试问哪个打工人不羡慕?但问题是有这样财富积累必然有像样的实力做靠山。没有人可以不劳而获。 看到这里,肯定有人说,那么对于普通人来说,卷可能真就成了唯一的出路。但是卷也有轻松的卷,“偷懒”的卷法,对于程序员而言,刨除掉一时无法改掉的开会传统占用的大部分时间,如何把有限的时间和精力放在真正重要的架构设计、需求设计上,而不是重复的造*,编码、改bug、手动测试。因此在科技改变生活的今天,学会使用AI工具成为程序员们的必备技能。 以全栈式全自动的软件开发工具飞算SoFlu软件机器人为例,作为全球首款面向微服务架构设计和最佳实践的软件机器人,SoFlu软件机器人改变了原来手工编码的作业模式,通过可视化拖拽方式以及参数配置就能实现等同于编写复杂代码的业务逻辑,在设计业务逻辑时就完成了微服务应用开发,做到“业务即图,图即代码”,实现“软件开发,十倍提效”。 而作为飞算SoFlu软件机器人的一个重要组成部分,FuncGPT(慧函数)支持所有类型函数创建。通过自然语言描述Java函数需求,实时生成高质量、高可读性的Java函数代码。生成代码可直接复制到IDEA,或一键导入Java全自动开发工具函数库。 FuncGPT(慧函数)具有五大核心能力: ● 自然语言:通过自然语言即可生成函数,降低软件开发门槛。 ● 秒级函数开发:全自动秒级开发函数,秒级完成,效率千倍提升。 ● 一站式函数开发应用:函数生成即可直接应用,效率、安全有保障。 ● 质量可靠:生成的函数代码符合业界规范、具备良好的可读性,符合最优解。 ● 灵活生成:可以根据开发人员的具体需求生成并支持调整和修改。 以“将文件流转换为File对象”这个需求为例,以下是FuncGPT(慧函数)秒级生成的代码: / 类名:FileConverter // 函数名:convertStreamToFile // 函数功能:将文件流转换为File对象 // POM依赖包:无 import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; public class FileConverter { /** * 将文件流转换为File对象 * @param inputStream 文件流 * @param filePath 文件路径 * @param fileName 文件名 * @return 转换后的File对象 * @throws IOException 如果发生I/O异常 */ public static File convertStreamToFile(InputStream inputStream, String filePath, String fileName) throws IOException { File file = new File(filePath + File.separator + fileName); // 创建File对象 try (FileOutputStream outputStream = new FileOutputStream(file)) { // 创建文件输出流 byte buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { // 从文件流读取数据并写入文件 outputStream.write(buffer, 0, bytesRead); } } return file; // 返回转换后的File对象 } } // 函数示例 // 将文件流转换为File对象示例 // 入参:inputStream,文件流 // 入参:filePath,文件路径 // 入参:fileName,文件名 // 出参:file,转换后的File对象 // 调用示例: // InputStream inputStream = new FileInputStream("example.txt"); // String filePath = "C:\\Users\\User\\Documents"; // String fileName = "example.txt"; // File file = FileConverter.convertStreamToFile(inputStream, filePath, fileName); // System.out.println(file.getAbsolutePath); // 输出结果:例如,将文件流转换为File对象后,文件的绝对路径为:C:\Users\User\Documents\example.txt // 则输出结果为:C:\Users\User\Documents\example.txt 通过分析,不难发现以上代码:
-
2023 云原生实用案例集 - 04 互联网 - 传鱼技术 基于函数计算,低成本高效率地支持每天 50W+ 的直播小程序
-
从中心到边缘--深入分析云原生边缘计算在地面上的痛点
-
读懂知乎,说它牛逼 !-从内容消费者到内容创造者,这种转变的开始只有一次,那就是被别人认可。 浏览量高的很大一部分原因是知乎上有很多程序员,关于互联网、计算机、编程等话题都会有大量曝光,这些相关内容时不时就会出现在热搜榜上。 今天,顺便给大家推荐九个程序员相关的高赞答案,这些答案我会不定期拿出来重温,喜欢的你也不妨收藏一下。(看完第九个答案你再来喜欢~)。 一 话题:程序员未来会是一个很内卷的职业吗?
-
一点 C# 到 C++ 的共享
-
给新手程序员学生购买笔记本电脑的一点建议