C++程序计算24点

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 

如果程序运行或者文章内容有误,欢迎在评论区指出。 

  • 9
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值