编程之美之24点

现在再看《编程之美》,昨天看到一个24点的问题:给玩家4张牌,每张面值在1~13之间,允许其中有数值相同的牌,采用加减乘除的方法,允许中间有小数,并且可以使用括号,但每张牌只能使用一次,试着构造一个多项式,使其运算结果为24;

这里首先想的就是利用穷举,就是先4个数的全排列,然后有三个符号,然后每个符号有四种可能,然后是有括号,这种方法比较复杂,所以就直接跳过了这个,书里给出了完整的代码,然后说一下比较优化的方法,采用分治方法,即把四个数当作一个集合A, 然后分成两个集合B1,B2,然后分别对B1和B2中的元素分别进行计算即F(B1),F(B2),然后在对B1,B2中的元素在进行分出子集,然后计算,最后,再将B1,B2的两个子集进行混合,进行计算。可以看出,这个就要利用递归解决了,为了把效率提高,减少重复的计算,也要采用动态规划的方法了,这里有个问题就是对于加法和乘法满足交换律,但是减法和除法是不满足的,所以一共需要进行六种运算;《编程之美》中对于子集的计算方法是利用二进制的方法,即4个数的总的集合是1111,然后如果有第一个数的集合就利用0001表示;

采用优化过的方法:

#include <cstdio>
#include <iostream>
#include <set>
#include <string>
#include <cmath>

using namespace std;

#define N 4
#define RES 24
#define EPS 1e-6

struct Elem
{
	double a;
	string s;

	Elem(double _a, string _s) : a(_a), s(_s){}
	Elem(double _a, char *_s) : a(_a), s(_s){}
	bool operator<(const Elem &m) const //这里注意一看是没注意后面加const
	{
		if( a < m.a )
		{
			return true;
		}
		return false;
	}
};

set<Elem> vset[1<<N];

set<Elem> & Fork(int s)
{
	if( vset[s].size() )//说明这组已经计算过了
	{
		return vset[s];
	}
	else
	{
		for(int i=1; i<=s/2; i++)//寻找子集 注意这里要是s的一半 要不就重复计算了
		{
			if( (i&s) == i )//只有在等于的时候说明是子集
			{
				set<Elem> &s1 = Fork(i);
				set<Elem> &s2 = Fork(s-i);
				
				for(set<Elem>::iterator m=s1.begin(); m!=s1.end(); m++)
				{
					for(set<Elem>::iterator n=s2.begin(); n!=s2.end(); n++)
					{
						string ss;
						double dd;//开始六种运算方式
						
						ss = "(" + m->s + "+" + n->s + ")";//实现加法
						dd = m->a + n->a;
						vset[s].insert( Elem(dd, ss) );

						ss = "(" + m->s + "-" + n->s + ")";	//实现减法
						dd = m->a - n->a;
						vset[s].insert( Elem(dd, ss) );

						ss = "(" + n->s + "-" + m->s + ")";	//实现减法
						dd = n->a - m->a;
						vset[s].insert( Elem(dd, ss) );

						ss = "(" + n->s + "*" + m->s + ")";	//实现乘法
						dd = n->a * m->a;
						vset[s].insert( Elem(dd, ss) );

						ss = "(" + n->s + "/" + m->s + ")";	//实现除法
						dd = n->a / m->a;
						vset[s].insert( Elem(dd, ss) );

						ss = '(' + m->s + '/' + n->s + ')';	//实现除法
						dd = m->a / n->a;
						vset[s].insert( Elem(dd, ss) );
					}
				}
			}
		}
		return vset[s];
	}	
}

int main()
{
	int d;
	char s[22];

	for(int i=0; i<4; i++)
	{
		cin>>d;
		sprintf(s, "%d", d);
		vset[1<<i].insert( Elem(d, s) );
	}

	Fork( (1<<N) - 1 );

	for(set<Elem>::iterator m=vset[(1<<N)-1].begin(); m!=vset[(1<<N)-1].end(); m++)
	{
		if( abs(m->a - RES) < EPS )
		{
			cout<<m->s<<endl;
		}
	}

	getchar();
	return 0;
}
    原文作者:adfa
    原文地址: https://blog.csdn.net/qiuchang008/article/details/22786827
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞