编程之美 1.16 24点游戏
24点游戏大家都知道:4张牌,可以进行+ - * / 四种运算,可以使用括号,每个牌用一次,任意组合构造表达式使结果为24。
扩展问题:n个整数,四种运算,可使用括号,每个数字使用一次,使表达式结果为 k
下面的算法1和算法2都是穷举,只是穷举的方式不一样,以下给出的两个算法代码都可以计算扩展问题。可能是集合操作原因,算法1的速度明显比算法2快
书上分析如下 本文地址
算法1:
算法1代码如下,我在原来的基础上做了一点改动1、从数组中任选两个数时,保证数对前面没有选择过; 2、通过运算符优先级去除多余的括号;3、选出的两个数相同时,减法和除法只做一次,即只做a-b ,a/b, 不做 b-a,b/a,其中1、3都可以减少冗余计算
调用函数PointGame即可返回表达式结果 本文地址
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<map> 5 #include<set> 6 #include<cmath> 7 #include<ctime> 8 using namespace std; 9 10 //cards[i]的值是通过表达式expr[i]计算而来 11 //且expr的最后一个运算操作lastop[i] 12 bool GameRecur(double cards[], string expr[], char lastop[], 13 const int cardsNum, const int result) 14 { 15 if(cardsNum == 1) 16 { 17 if(cards[0] == result) 18 { 19 //cout<<expr[0]<<endl; 20 return true; 21 } 22 else return false; 23 } 24 //从已有数中任选两个,计算得到中间结果,并且和剩余的数一起存在cards数组的前 25 //cardsNum-1个位置 26 map<pair<double,double>,bool> selectedPair; 27 for(int i = 0; i<cardsNum; i++) 28 { 29 for(int k = i+1; k < cardsNum; k++) 30 { 31 if( selectedPair.find(pair<double, double>(cards[i], cards[k])) 32 != selectedPair.end() || selectedPair.find(pair<double, double> 33 (cards[k], cards[i])) != selectedPair.end() ) 34 break; 35 else 36 { 37 selectedPair[pair<double,double>(cards[i], cards[k])] = 1; 38 selectedPair[pair<double,double>(cards[k], cards[i])] = 1; 39 } 40 //上面的代码保证选出的数对不重复 41 double a = cards[i], b = cards[k]; 42 cards[k] = cards[cardsNum-1]; 43 string expra = expr[i], exprb = expr[k]; 44 expr[k] = expr[cardsNum-1]; 45 char lastop_a = lastop[i], lastop_b = lastop[k]; 46 lastop[k] = lastop[cardsNum-1]; 47 48 cards[i] = a + b; 49 expr[i] = expra + '+' + exprb; 50 lastop[i] = '+'; 51 if(GameRecur(cards, expr, lastop, cardsNum-1, result)) 52 return true; 53 54 cards[i] = a - b; 55 if('+' == lastop_b || '-' == lastop_b) 56 expr[i] = expra + '-' + '(' + exprb + ')'; 57 else expr[i] = expra + '-' + exprb; 58 lastop[i] = '-'; 59 if(GameRecur(cards, expr, lastop, cardsNum-1, result)) 60 return true; 61 62 if(a != b) 63 { 64 cards[i] = b - a; 65 if('+' == lastop_a || '-' == lastop_a) 66 expr[i] = exprb + '-' + '(' + expra + ')'; 67 else expr[i] = exprb + '-' + expra; 68 lastop[i] = '-'; 69 if(GameRecur(cards, expr, lastop, cardsNum-1, result)) 70 return true; 71 } 72 73 cards[i] = a * b; 74 if('-' == lastop_a || '+' == lastop_a) 75 expr[i] = '(' + expra + ')' + '*'; 76 else expr[i] = expra + '*'; 77 if('*' == lastop_b || ' ' == lastop_b) 78 expr[i] += exprb; 79 else expr[i] += '(' + exprb + ')'; 80 lastop[i] = '*'; 81 if(GameRecur(cards, expr, lastop, cardsNum-1, result)) 82 return true; 83 84 if(b != 0) 85 { 86 cards[i] = a / b; 87 if('-' == lastop_a || '+' == lastop_a) 88 expr[i] = '(' + expra + ')' + '/'; 89 else expr[i] = expra + '/'; 90 if(' ' == lastop_b) 91 expr[i] += exprb; 92 else expr[i] += '(' + exprb + ')'; 93 lastop[i] = '/'; 94 if(GameRecur(cards, expr, lastop, cardsNum-1, result)) 95 return true; 96 } 97 98 if(a != 0 && a!= b) 99 { 100 cards[i] = b / a; 101 if('-' == lastop_b || '+' == lastop_b) 102 expr[i] = '(' + exprb + ')' + '/'; 103 else expr[i] = exprb + '/'; 104 if(' ' == lastop_a) 105 expr[i] += expra; 106 else expr[i] += '(' + expra + ')'; 107 lastop[i] = '/'; 108 if(GameRecur(cards, expr, lastop, cardsNum-1, result)) 109 return true; 110 } 111 112 //把选出来的两个数放回原位 113 cards[i] = a; 114 cards[k] = b; 115 expr[i] = expra; 116 expr[k] = exprb; 117 lastop[i] = lastop_a; 118 lastop[k] = lastop_b; 119 } 120 } 121 return false; 122 } 123 124 //cards 输入的牌 125 //cardsNum 牌的数目 126 //result 想要运算得到的结果 127 string PointGame(int cards[], const int cardsNum, const int result) 128 { 129 string expr[cardsNum]; 130 char lastop[cardsNum]; 131 double cardsCopy[cardsNum]; 132 for(int i = 0; i < cardsNum; i++) 133 { 134 char buf[30]; 135 sprintf(buf, "%d", cards[i]); 136 expr[i] = buf; 137 lastop[i] = ' ';//表示cardsCopy[i]是不经过任何运算的原始数据 138 cardsCopy[i] = cards[i]; 139 } 140 if(GameRecur(cardsCopy, expr, lastop, cardsNum, result)) 141 return expr[0]; 142 else return "-1"; 143 }
对算法1稍作修改可以输出全部的表达式:
1 //------------------------------------------------------------算法1输出所有组合 2 //cards[i]的值是通过表达式expr[i]计算而来 3 //且expr的最后一个运算操作lastop[i] 4 bool GameRecur_all(double cards[], string expr[], char lastop[], 5 const int cardsNum, const int result) 6 { 7 if(cardsNum == 1) 8 { 9 if(cards[0] == result) 10 { 11 cout<<expr[0]<<endl; 12 return true; 13 } 14 else return false; 15 } 16 //从已有数中任选两个,计算得到中间结果,并且和剩余的数一起存在cards数组的前 17 //cardsNum-1个位置 18 map<pair<double,double>,bool> selectedPair; 19 bool res = false; 20 for(int i = 0; i<cardsNum; i++) 21 { 22 for(int k = i+1; k < cardsNum; k++) 23 { 24 if( selectedPair.find(pair<double, double>(cards[i], cards[k])) 25 != selectedPair.end() || selectedPair.find(pair<double, double> 26 (cards[k], cards[i])) != selectedPair.end() ) 27 break; 28 else 29 { 30 selectedPair[pair<double,double>(cards[i], cards[k])] = 1; 31 selectedPair[pair<double,double>(cards[k], cards[i])] = 1; 32 } 33 //上面的代码保证选出的数对不重复 34 double a = cards[i], b = cards[k]; 35 cards[k] = cards[cardsNum-1]; 36 string expra = expr[i], exprb = expr[k]; 37 expr[k] = expr[cardsNum-1]; 38 char lastop_a = lastop[i], lastop_b = lastop[k]; 39 lastop[k] = lastop[cardsNum-1]; 40 41 cards[i] = a + b; 42 expr[i] = expra + '+' + exprb; 43 lastop[i] = '+'; 44 res = GameRecur_all(cards, expr, lastop, cardsNum-1, result) 45 || res; 46 47 cards[i] = a - b; 48 if('+' == lastop_b || '-' == lastop_b) 49 expr[i] = expra + '-' + '(' + exprb + ')'; 50 else expr[i] = expra + '-' + exprb; 51 lastop[i] = '-'; 52 res = GameRecur_all(cards, expr, lastop, cardsNum-1, result) || res; 53 54 if(a != b) 55 { 56 cards[i] = b - a; 57 if('+' == lastop_a || '-' == lastop_a) 58 expr[i] = exprb + '-' + '(' + expra + ')'; 59 else expr[i] = exprb + '-' + expra; 60 lastop[i] = '-'; 61 res = GameRecur_all(cards, expr, lastop, cardsNum-1, result) 62 || res; 63 } 64 65 cards[i] = a * b; 66 if('-' == lastop_a || '+' == lastop_a) 67 expr[i] = '(' + expra + ')' + '*'; 68 else expr[i] = expra + '*'; 69 if('*' == lastop_b || ' ' == lastop_b) 70 expr[i] += exprb; 71 else expr[i] += '(' + exprb + ')'; 72 lastop[i] = '*'; 73 res = GameRecur_all(cards, expr, lastop, cardsNum-1, result) 74 || res; 75 76 if(b != 0) 77 { 78 cards[i] = a / b; 79 if('-' == lastop_a || '+' == lastop_a) 80 expr[i] = '(' + expra + ')' + '/'; 81 else expr[i] = expra + '/'; 82 if(' ' == lastop_b) 83 expr[i] += exprb; 84 else expr[i] += '(' + exprb + ')'; 85 lastop[i] = '/'; 86 res = GameRecur_all(cards, expr, lastop, cardsNum-1, result) 87 || res; 88 } 89 90 if(a != 0 && a!= b) 91 { 92 cards[i] = b / a; 93 if('-' == lastop_b || '+' == lastop_b) 94 expr[i] = '(' + exprb + ')' + '/'; 95 else expr[i] = exprb + '/'; 96 if(' ' == lastop_a) 97 expr[i] += expra; 98 else expr[i] += '(' + expra + ')'; 99 lastop[i] = '/'; 100 res = GameRecur_all(cards, expr, lastop, cardsNum-1, result) 101 || res; 102 } 103 104 //把选出来的两个数放回原位 105 cards[i] = a; 106 cards[k] = b; 107 expr[i] = expra; 108 expr[k] = exprb; 109 lastop[i] = lastop_a; 110 lastop[k] = lastop_b; 111 } 112 } 113 return res; 114 } 115 116 //cards 输入的牌 117 //cardsNum 牌的数目 118 //result 想要运算得到的结果 119 void PointGame_all(int cards[], const int cardsNum, const int result) 120 { 121 string expr[cardsNum]; 122 char lastop[cardsNum]; 123 double cardsCopy[cardsNum]; 124 for(int i = 0; i < cardsNum; i++) 125 { 126 char buf[30]; 127 sprintf(buf, "%d", cards[i]); 128 expr[i] = buf; 129 lastop[i] = ' ';//表示cardsCopy[i]是不经过任何运算的原始数据 130 cardsCopy[i] = cards[i]; 131 } 132 if(GameRecur_all(cardsCopy, expr, lastop, cardsNum, result) == false) 133 cout<<"-1\n"; 134 }
算法2
算法2代码如下,集合用stl中的set表示,代码中变量名保持和书中一致
调用函数PointGame2即可返回表达式结果,调用方式同算法1 本文地址
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<map> 5 #include<set> 6 #include<cmath> 7 #include<ctime> 8 using namespace std; 9 10 struct setNode 11 { 12 double val; 13 string expr;//运算出val的表达式 14 setNode(double v, string e):val(v),expr(e){} 15 setNode(double v, char e[]):val(v),expr(e){} 16 bool operator < (const setNode &s)const 17 { 18 return val < s.val; 19 } 20 }; 21 22 void f(const int i, set<setNode> s[], const double result) 23 { 24 int len = sizeof(s)/sizeof(set<setNode>) - 1;//len = 2^n - 1 25 if(s[i].empty() == true) 26 for(int x = 1; x <= i/2; x++) 27 if((x&i) == x) 28 { 29 set<setNode>::iterator it1, it2; 30 // s[i] U= fork(s[x] ,s[i-x]) 31 for(it1 = s[x].begin(); it1 != s[x].end(); it1++) 32 for(it2 = s[i-x].begin(); it2 != s[i-x].end(); it2++) 33 { 34 string expr; 35 double tempresult; 36 tempresult = it1->val + it2->val; 37 expr = '(' + it1->expr + '+' + it2->expr + ')'; 38 s[i].insert(setNode(tempresult, expr)); 39 //计算f[2^n-1]时,若计算好了结果则可以停止 40 if(i == len && tempresult == result)return; 41 42 43 tempresult = it1->val - it2->val; 44 expr = '(' + it1->expr + '-' + it2->expr + ')'; 45 s[i].insert(setNode(tempresult, expr)); 46 if(i == len && tempresult == result)return; 47 if(it1->val != it2->val) 48 { 49 tempresult = it2->val - it1->val; 50 expr = '(' + it2->expr + '-' + it1->expr + ')'; 51 s[i].insert(setNode(tempresult, expr)); 52 if(i == len && tempresult == result)return; 53 } 54 55 tempresult = it1->val * it2->val; 56 expr = '(' + it1->expr + '*' + it2->expr + ')'; 57 s[i].insert(setNode(tempresult, expr)); 58 if(i == len && tempresult == result)return; 59 60 if(it2->val != 0) 61 { 62 tempresult = it1->val / it2->val; 63 expr = '(' + it1->expr + '/' + it2->expr + ')'; 64 s[i].insert(setNode(tempresult, expr)); 65 if(i == len && tempresult == result)return; 66 } 67 if(it1->val != it2->val && it1->val != 0) 68 { 69 tempresult = it2->val / it1->val; 70 expr = '(' + it2->expr + '/' + it1->expr + ')'; 71 s[i].insert(setNode(tempresult, expr)); 72 if(i == len && tempresult == result)return; 73 } 74 } 75 } 76 } 77 78 string PointGame2(int cards[], const int cardsNum, const int result) 79 { 80 int len = 1<<cardsNum; 81 set<setNode> S[len]; //对应文章中的数组S 82 //初始化只有一个元素的子集,j = 2^i,即只有一个2进制位是1 83 for(int i = 0, j = 1; i < cardsNum; i++, j = j<<1) 84 { 85 char buf[30]; 86 sprintf(buf, "%d", cards[i]); 87 S[j].insert(setNode(cards[i],buf)); 88 } 89 for(int i = 1; i <= len - 1; i++) 90 f(i, S, result); 91 for(set<setNode>::iterator it = S[len-1].begin(); 92 it != S[len-1].end(); it++) 93 { 94 if(it->val == result) 95 return it->expr; 96 } 97 return "-1"; 98 }
【版权声明】转载请注明出处:http://www.cnblogs.com/TenosDoIt/p/3407636.html