24点算法



/*
	给定四个数,计算这四个数(1-13)的'+','-','*','/'能不能得到24(占时只实现满足一种情况就打印)
	
	方法:穷举法
	思路:有两种方式,(1)"连贯式计算"(2)"分隔式计算"
	(1)连贯式计算、例如对于2+4*3+5的计算,2+4=6 => 6*3=18 => 18+5=23 (2+4)*3+5
	(2)分隔式计算、例如对于2+4*3+5的计算,2+4=6 => 3+5=8 => 6*8=48 (2+4)*(3+5)

	calc_24()方法,用三个for循环对四个数进行排列,又用三个for对三个符号进行循环,
	因为符号可以重复,所以每个符号都有四种选择。调用calc_consecutive方法和cale_separate
	方法判断是否为24.
	calc_consecutive()方法,计算连贯式的结果,就是每两个两个的计算,从左向右
	cale_separate()方法,计算分隔式的结果,就是先前两个的结果,然后后两个,最后一起的结果。

	这两上方法都会调用calc()方法,来计算两个数的结果,要对结果进行判断,比如相减的结果要
	大于0,相除时,除数不能为0,并且不能有余数。

	add_parenthesis()方法是加括号

	get_consecutive_exp()方法对连贯式加括号,因为我们是用0,1,2,3,分别表示“+”,"-","*","/"
	所以if(oper[0]<2 && oper[1]>=2)这句话是判断第一个操作符是"+"或"-",然后第二个操作符是"*"或"/",
	因为第二个操作符的优先级比较高,所以前两个数字要加括号

	get_separate_exp()方法是对分隔式加括号,这种比较简单,直接前两个,后两个加括号就可以了
	
*/
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

char operator_char_def[4]={'+','-','*','/'};
int count=0;

int calc(int n1,int n2,char oper)
{
	if(oper<0 || oper>4)
		return -1;
	switch(operator_char_def[oper])
	{
		case '+':
			return n1+n2;
		case '-':
			if(n1<n2)
				return -1;
			return n1-n2;
		case '*':
			return n1*n2;
		case '/':
			if((n2==0)||(n1%n2!=0))
				return -1;
			return n1/n2;
	}
	return -1;
}

int	calc_consecutive(int n[4],char oper[3])
{
	int r1 = calc(n[0],n[1],oper[0]);
	if(r1<0)
		return 0;
	int r2 = calc(r1,n[2],oper[1]);
	if(r2<0)
		return 0;
	int r = calc(r2,n[3],oper[2]);
	return r;
}

int cale_separate(int n[4],char oper[3])
{
	int r1 = calc(n[0],n[1],oper[0]);
	if(r1<0)
		return 0;
	int r2 = calc(n[2],n[3],oper[2]);
	if(r2<0)
		return 0;
	int r = calc(r1,r2,oper[1]);
	return r;
}

void tmp_output(int method,int n[4],char oper[3])
{
	cout<<((method==1)?"consercutive":"separate")<<endl;
	cout<<n[0];
	for(int i=1;i<4;i++)
	{
		cout<<operator_char_def[oper[i-1]]<<n[i];
	}
	cout<<endl;
}

void add_parenthesis(stringstream& ss)
{
	ss.str("("+ss.str()+")");
	ss.seekp(0,ios_base::end);
}

string get_consecutive_exp(int n[4],char oper[3])
{
	stringstream ss;
	ss<<n[0]<<operator_char_def[oper[0]]<<n[1];
	if(oper[0]<2 && oper[1]>=2)
		add_parenthesis(ss);
	ss<<operator_char_def[oper[1]]<<n[2];
	if(oper[1]<2 && oper[2]>=2)
		add_parenthesis(ss);
	ss<<operator_char_def[oper[2]]<<n[3];
	return ss.str();
}

string get_separate_exp(int n[4],char oper[3])
{
	stringstream ss;
	ss << '(' << n[0] << operator_char_def[oper[0]] << n[1] << ')'<< operator_char_def[oper[1]] 
		<< '(' << n[2] << operator_char_def[oper[2]] << n[3] << ')';
	return ss.str();
}

bool calc_24(int card[4])
{
	int n[4];
	char oper[3];
	for(int i1=0;i1<4;i1++)
	{
		n[0]=card[i1];
		for(int i2=0;i2<4;i2++)
		{
			if(i2==i1)
				continue;
			n[1]=card[i2];
			for(int i3=0;i3<4;i3++)
			{
				if(i3==i1||i3==i2)
					continue;
				n[2]=card[i3];
				int i4 = 6-i1-i2-i3;
				n[3]=card[i4];

				for(int j1=0;j1<4;j1++)
				{
					oper[0]=j1;	
					for(int j2=0;j2<4;j2++)
					{
						oper[1]=j2;
						for(int j3=0;j3<4;j3++)
						{
							oper[2]=j3;
							if(calc_consecutive(n,oper)==24)
							{
								//tmp_output(1,n,oper);
								cout<<get_consecutive_exp(n,oper)<<endl;
								return true;
							}
							if(cale_separate(n,oper)==24)
							{
								//tmp_output(2,n,oper);
								cout<<get_separate_exp(n,oper)<<endl;
								return true;
							}
						}
					}
				}
			}
		}
	}
	return false;
}

int main()
{
	int num[4];
	cout<<"Press Ctrl+C to exit"<<endl;
	while(scanf("%d %d %d %d",&num[0],&num[1],&num[2],&num[3])!=EOF)
	{
		if(!calc_24(num))
		{
			cout<<"No result"<<endl;
		}
	}

	return 0;
}


对于打印多个符合条件的表达式的情况,只需要改几个地方

1、判断完是24后,打印完,不返回,所以calc_24()方法要修改下

2、如果要增加减法可以负数的情况,则把cale_separate(),calc_consecutive(),还有calc()方法中所有判断小于0的情况去掉即可

3、如果要增加除法可以为小数的情况,则需要把int改为float,并且去掉calc()方法中判断除法不为整数的条件即可


你可能感兴趣的:(24点算法,24点的实现)