编程之美: 第一章 1.16 24点游戏

来源:互联网 发布:ida pro 7.0 mac 编辑:程序博客网 时间:2024/06/10 06:45
/*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;}

0 0
原创粉丝点击