/*
24点游戏:
给玩家4张牌,每张牌的面值在1~13之间,允许其中有数值相同的牌。采用加减乘除,允许中间运算存在小数,并且可以使用括号,但每张牌只能使用一次,尝试
构造表达式,使其运算结果为24.
输入:n1,n2,n3,n4
输出:若能得到运算结果为24,输出对应表达式
输入:
11,8,3,5
输出:
(11-8)*(3+5)=24
解法1:
穷举法,运算符号4种,每个数字使用一次
每个数只能使用一次,对4个数进行全排列,共有4!=24种,4个数的四则运算需要3个运算符,共有64种,总有有24*64 = 1536种
再考虑加括号情况:4个数加括号共有5种情形
(A (B (CD))),(A ((BC) D)),((AB) (CD)),( ( A (BC) ) D ),(((AB)C)D)
共有24 * 64 * 5 = 7680种,采用逆波兰表达式,总数不变。
递归解法:
将给定的4个数放入数组Array中,将其作为参数穿入函数f中,
f(Array)
{
if(Array.length < 2)
{
if(得到的最终结果为24)
输出表达式
else
输出无法构造符合要求的表达式
}
foreach(从数组中任取两个数的组合)
{
foreach(运算符(+,-,*,/))
{
1计算该组合在此运算符下的结果
2将该组合中的两个数从原数组中移除,并按步骤1的计算结果放入数组
3对新数组调用f,找到表达式则返回
4将步骤1的结果移除,并将该组合中的两个数重新放回到数组中对应的位置
}
}
}
解法2:
定义要计算的初始数据,放于集合A中,定义函数f(A)为对集合A中的元素进行所有可能的四则运算所得到的值,采用分治思想,先将A划分为两个子集A1和A-A1,
其中A1为A的非空真自己,分别计算A1和A-A1中的元素进行四则运算得到的结果集合,即f(A1)和f(A-A1),然后对f(A1)和f(A-A1)这两个集合中的元素进行加减
乘除运算,最后得到的所有集合的并集就是f(A)。
分治:划分,递归求解,合并
给定两个多重集合A和B,定义两个集合中的元素如下:
Fork(A,B) = 并{a+b,a-b,b-a,a*b,a/b(b!=0),b/a(a!=0)},(a,b)属于A*B,假设A1中有n个元素,A2中有m个元素,那么将有n*m个(a,b),而每对值需要进行6个计算,
Fork(A1,A2) = 2*n*m个元素,需要去重。假设集合A中有n个元素,那么集合A的所有非空真子集个数为2^n-2,则f(A)第一层递推式中共有(2^n-2)/2个Fork函数
可以用二进制数来表示集合和子集,由于只有4个元素,可以采用4位的二进制数来表示集合A及其真子集,设A{a0,a1,a2,a3},当且仅当ai在某一个真子集中时,
该真子集所代表的二进制数对应的第i位才为1,如A1 = {a1,a2,a3}则1110表示A1,若A2 = {a0,a3},那么1001表示A2,所以A的真子集范围为1到14(1到2^n-2),
再用一个大小为2^n-1的数组S来保存f(i)(1<=i<=15),数组S中的每一个元素S[i]都是一个集合(f(i)),其中S[2^n-1]即为集合A中的所有元素通过四则运算和加括号得到
的全部结果,通过检查S[2^n-1],可得知某个输入是否有解。
24Game(Array)//Array为初始输入集合
{
for(int i = 1 ; i <= 2^n-1 ; i++)
{
S[i] = 空集;//初始化将S中的各个集合置为空集,n为集合Array的元素个数,在24点中级为4,
}
for(int i = 1 ; i < n ; i++)
{
S[2^i] = {ai};//先对每个只有一个元素的真子集赋值,即为该元素本身
}
for(int i = 1 ;i <= 2^n - 1 ; i++)//对每个i都代表着Array的一个真子集
{
S[i] = f(i);//
}
Check(S[2^n-1]);//检查S[2^n-1]中是否有值为24的元素,并返回
}
f(int i)//i的二进制表示可代表着集合的一个真子集
{
if(S[i] != 空集)
{
return S[i];
}
for(int x = 1 ; x < i ; i++)//只有小于i的x才可能称为i的真子集
{
if((x & i) == x)//&为与运算,只有当x & i == x成立时,x才为i的子集,此时i-x为i的另一个真子集,x与i-x共同构成i的一个划分
{
S[i] U= Fork(f(x),f(i-x));//U为集合的bing yunsuan ,Fork的过程中,去除重复中间结果
}
}
}
*/
#include <stdio.h> #include <math.h> #include <string> #include <iostream> #include <stdlib.h> using namespace std; const double Threshould = 1E-6;//浮点数的误差值 const int CardsNumber = 4; const int ResultValue = 24; double number[CardsNumber]; string result[CardsNumber]; bool dot(int n) { if(n == 1)//递归出口 { if(fabs(number[0] - ResultValue) < Threshould) { cout << result[0] <<endl; } else { return false; } } for(int i = 0 ;i < n ; i++) { for(int j = i + 1 ; j < n ; j++) { double a,b; string expa,expb; a = number[i]; b = number[j]; number[j] = number[n-1];//? expa = result[i]; expb = result[j]; result[j] = result[n-1];//? result[i] = '(' + expa + '+' + expb + ')'; number[i] = a + b; if(dot(n-1)) { return true; } result[i] = '(' + expa + '-' + expb + ')'; number[i] = a - b; if(dot(n-1)) { return true; } result[i] = '(' + expa + '-'+ expb + ')'; number[i] = b - a ; if(dot(n-1)) { return true; } result[i] = '(' + expa + '*'+ expb + ')'; number[i] = a * b; if(dot(n-1)) { return true; } if(b != 0) { result[i] = '(' + expa + '/'+ expb + ')'; number[i] = a / b; if(dot(n-1)) { return true; } } if(a != 0) { result[i] = '(' + expa + '/'+ expb + ')'; number[i] = b / a; if(dot(n-1)) { return true; } } number[i] =a ; number[j] = b; result[i] = expa; result[j] = expb; } } return false; } void process() { int x; for(int i = 0 ; i < CardsNumber ; i++) { char buffer[20]; cout << "the" << i << "th number:"; cin >> x; number[i] = x; itoa(x,buffer,10); result[i] = buffer; } if(dot(CardsNumber)) { cout<< "成功了" <<endl; } else { cout<< "失败了" << endl; } } int main(int argc,char* argv[]) { process(); getchar(); system("pause"); return 0; }