24点
24点是一个非常经典的游戏,从扑克牌里抽4张牌,其中J=11,Q=12,K=13,然后经过+,-,*,/,(),的计算后,使得计算得值为24,例如抽到1,2,2,5四张牌,那么
(1+5)*(2+2)=24;
这就是可以凑成24点的一种情况,作为一个经典题目,在leetcode上也有对应的题目进行练习
PS 看见知乎大佬有一种必24点的算法,但是要用到阶乘和次方 式子为(a0+b0+c0+d0)! =24
一、总体思路
1.因为是简单暴力向的,所以我们的做法就是直接穷举出所有可能的情况,首先是考虑四个数a,b,c,d的排列情况
如b,a,c,d等等,通过排列组合可以得到 4*3*2*1 = 24 种情况
2.然后考虑a,b,c,d中的三个运算符的情况设一个自定义的运算符为$,$可以是+,-,*,/中的任意一个
则有 a$b$c$d 这个式子,同样,运算符的可能性有 3*4 = 12 种
3.最后考虑()的情况,我们规定,每次一对()只框住两个数,比如a+b+c+d =(((a+b)+c)+d) = ((r1+c)+d)=(r2+d)=r3(其中r1=a+b,r2=r1+c,r3=r2+d)
()的情况其实就是运算优先级的问题,无论运算符是什么,都一定是先运算括号里的内容
所以我们可以穷举出情况
第一种r1=a$b,r2=r1$c,r3=r2$d;
第二种r1=b$c,r2=a$r1,r3=r2$d;
第三种r1=b$c,r2=r1$d,r3=a$r2;
第四种r1=c$d,r2=b$r1,r3=a$r2;
第五种r1=a$b,r2=c$d,r3=r1$r2;
仔细观察不难发现,我们控制了运算符和数字的绝对顺序从左到右的顺序严格是a$b$c$d,不论任何情况都不会改变abcd的顺序,是因为我们在上面已经排出来了所有的24种情况,所以我们这就可以严格控制abcd的顺序了
二、代码实现
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 int mark_int[4] = { 1,2,3,4 }; 5 string mark_char = "+-*/"; 6 double cal(double a, int m, double b) 7 { 8 switch (m) 9 { 10 case 1: return a + b; 11 case 2: return a - b; 12 case 3: return a * b; 13 case 4: return a / b; 14 } 15 } 16 17 bool cal1(double a, double b, double c, double d, int m1, int m2, int m3) 18 { 19 double r1; 20 double r2; 21 double r3; 22 r1 = cal(a, m1, b); 23 r2 = cal(r1, m2, c); 24 r3 = cal(r2, m3, d); 25 if (r3 == 24) 26 { 27 cout << "(((" << a << mark_char[m1 - 1] << b << ")" << mark_char[m2 - 1] << c << ")" << mark_char[m3 - 1] << d << ")" << endl; 28 return 1; 29 } 30 return 0; 31 } 32 33 bool cal2(int a, int b, int c, int d, int m1, int m2, int m3) 34 { 35 double r1; 36 double r2; 37 double r3; 38 r1 = cal(b, m1, c); 39 r2 = cal(a, m2, r1); 40 r3 = cal(r2, m3, d); 41 if (r3 == 24) 42 { 43 cout << "((" << a << mark_char[m1 - 1] << "(" << b << mark_char[m2 - 1] << c << "))" << mark_char[m3 - 1] << d << ")" << endl; 44 return 1; 45 } 46 return 0; 47 } 48 49 bool cal3(int a, int b, int c, int d, int m1, int m2, int m3) 50 { 51 double r1; 52 double r2; 53 double r3; 54 r1 = cal(b, m1, c); 55 r2 = cal(r1, m2, d); 56 r3 = cal(a, m3, r2); 57 if (r3 == 24) 58 { 59 cout << "(" << a << mark_char[m1 - 1] << "((" << b << mark_char[m2 - 1] << c << ")" << mark_char[m3 - 1] << d << "))" << endl; 60 return 1; 61 } 62 return 0; 63 } 64 65 bool cal4(int a, int b, int c, int d, int m1, int m2, int m3) 66 { 67 double r1; 68 double r2; 69 double r3; 70 r1 = cal(c, m1, d); 71 r2 = cal(b, m2, r1); 72 r3 = cal(a, m3, r2); 73 if (r3 == 24) 74 { 75 cout << "(" << a << mark_char[m1 - 1] << "(" << b << mark_char[m2 - 1] << "(" << c << mark_char[m3 - 1] << d << ")))" << endl; 76 return 1; 77 } 78 return 0; 79 } 80 81 bool cal5(int a, int b, int c, int d, int m1, int m2, int m3) 82 { 83 double r1; 84 double r2; 85 double r3; 86 r1 = cal(a, m1, b); 87 r2 = cal(c, m3, d); 88 r3 = cal(r1, m2, r2); 89 if (r3 == 24) 90 { 91 cout << "((" << a << mark_char[m1 - 1] << b << ")" << mark_char[m2 - 1] << "(" << c << mark_char[m3 - 1] << d << "))" << endl; 92 return 1; 93 } 94 return 0; 95 } 96 97 98 bool all_cal(int a, int b, int c, int d) 99 { 100 for (int i = 1; i <= 4; i++) 101 for (int j = 1; j <= 4; j++) 102 for (int k = 1; k <= 4; k++) 103 { 104 if (cal1(a, b, c, d, i, j, k) == true || cal2(a, b, c, d, i, j, k) == true || cal3(a, b, c, d, i, j, k) == true || cal4(a, b, c, d, i, j, k) == true || cal5(a, b, c, d, i, j, k) == true) 105 return 1; 106 } 107 return 0; 108 } 109 110 111 bool judge(int a, int b, int c, int d) 112 { 113 int all[24][4] = { 114 {a,b,c,d},{a,b,d,c},{a,c,b,d},{a,c,d,b},{a,d,b,c},{a,d,c,b}, 115 {b,a,c,d},{b,a,d,c},{b,c,a,d},{b,c,d,a},{b,d,a,c},{b,d,c,a}, 116 {c,a,b,d},{c,a,d,b},{c,b,a,d},{c,b,d,a},{c,d,a,b},{c,d,b,a}, 117 {d,a,b,d},{d,a,d,b},{d,b,a,c},{d,b,c,a},{d,c,a,b},{d,c,b,a}, 118 }; 119 for (int i = 0; i < 24; i++) 120 { 121 if (all_cal(all[i][0], all[i][1], all[i][2], all[i][3])) 122 return 1; 123 } 124 return 0; 125 } 126 127 int main() 128 { 129 int a, b, c, d; 130 cin >> a >> b >> c >> d; 131 if (!judge(a, b, c, d)) 132 cout << "凑不成24点" << endl; 133 134 }
三、代码解释
先做一个计算两个数的函数,用数组int mark_int[4] = {1,2,3,4}的四个数表示+ - * /,string mark_char是用来最后显示的
1 int mark_int[4] = { 1,2,3,4 }; 2 string mark_char = "+-*/"; 3 double cal(double a, int m, double b) 4 { 5 switch (m)//用switch来进行运算符的选择 6 { 7 case 1: return a + b; 8 case 2: return a - b; 9 case 3: return a * b; 10 case 4: return a / b; 11 } 12 }
我们在实现五种括号的函数,并且我们规定运算一定是 a m1 b m2 c m3 d(m1,m2,m3是三个运算符的代号),意思就是abcd的从左到右顺序不乱,m1m2m3从左到右的顺序也不会乱,比较粗暴的理解就是ab之间一定是m1,bc之间一定是m2,cd之间一定其实m3,然后如果成功返回运算的过程和true,否则返回false
1 bool cal1(double a, double b, double c, double d, int m1, int m2, int m3) 2 { 3 double r1; 4 double r2; 5 double r3; 6 r1 = cal(a, m1, b); 7 r2 = cal(r1, m2, c); 8 r3 = cal(r2, m3, d); 9 if (r3 == 24) 10 { 11 cout << "(((" << a << mark_char[m1 - 1] << b << ")" << mark_char[m2 - 1] << c << ")" << mark_char[m3 - 1] << d << ")" << endl; 12 return 1; 13 } 14 return 0; 15 }//第一种r1=a$b,r2=r1$c,r3=r2$d; 16 17 bool cal2(int a, int b, int c, int d, int m1, int m2, int m3) 18 { 19 double r1; 20 double r2; 21 double r3; 22 r1 = cal(b, m1, c); 23 r2 = cal(a, m2, r1); 24 r3 = cal(r2, m3, d); 25 if (r3 == 24) 26 { 27 cout << "((" << a << mark_char[m1 - 1] << "(" << b << mark_char[m2 - 1] << c << "))" << mark_char[m3 - 1] << d << ")" << endl; 28 return 1; 29 } 30 return 0; 31 }//第二种r1=b$c,r2=a$r1,r3=r2$d; 32 33 bool cal3(int a, int b, int c, int d, int m1, int m2, int m3) 34 { 35 double r1; 36 double r2; 37 double r3; 38 r1 = cal(b, m1, c); 39 r2 = cal(r1, m2, d); 40 r3 = cal(a, m3, r2); 41 if (r3 == 24) 42 { 43 cout << "(" << a << mark_char[m1 - 1] << "((" << b << mark_char[m2 - 1] << c << ")" << mark_char[m3 - 1] << d << "))" << endl; 44 return 1; 45 } 46 return 0; 47 }//第三种r1=b$c,r2=r1$d,r3=a$r2; 48 49 bool cal4(int a, int b, int c, int d, int m1, int m2, int m3) 50 { 51 double r1; 52 double r2; 53 double r3; 54 r1 = cal(c, m1, d); 55 r2 = cal(b, m2, r1); 56 r3 = cal(a, m3, r2); 57 if (r3 == 24) 58 { 59 cout << "(" << a << mark_char[m1 - 1] << "(" << b << mark_char[m2 - 1] << "(" << c << mark_char[m3 - 1] << d << ")))" << endl; 60 return 1; 61 } 62 return 0; 63 }//第四种r1=c$d,r2=b$r1,r3=a$r2; 64 65 bool cal5(int a, int b, int c, int d, int m1, int m2, int m3) 66 { 67 double r1; 68 double r2; 69 double r3; 70 r1 = cal(a, m1, b); 71 r2 = cal(c, m3, d); 72 r3 = cal(r1, m2, r2); 73 if (r3 == 24) 74 { 75 cout << "((" << a << mark_char[m1 - 1] << b << ")" << mark_char[m2 - 1] << "(" << c << mark_char[m3 - 1] << d << "))" << endl; 76 return 1; 77 } 78 return 0; 79 }//第五种r1=a$b,r2=c$d,r3=r1$r2;
接下来是12种的符号的排列情况,如果有一种括号情况满足,我们就返回true,否则返回false
1 bool all_cal(int a, int b, int c, int d) 2 { 3 for (int i = 1; i <= 4; i++) 4 for (int j = 1; j <= 4; j++) 5 for (int k = 1; k <= 4; k++) 6 { 7 if (cal1(a, b, c, d, i, j, k) == true || cal2(a, b, c, d, i, j, k) == true || cal3(a, b, c, d, i, j, k) == true || cal4(a, b, c, d, i, j, k) == true || cal5(a, b, c, d, i, j, k) == true) 8 return 1; 9 } 10 return 0; 11 }
最后是在总判断函数中写入24种的abcd排列情况
1 bool judge(int a, int b, int c, int d) 2 { 3 int all[24][4] = { 4 {a,b,c,d},{a,b,d,c},{a,c,b,d},{a,c,d,b},{a,d,b,c},{a,d,c,b}, 5 {b,a,c,d},{b,a,d,c},{b,c,a,d},{b,c,d,a},{b,d,a,c},{b,d,c,a}, 6 {c,a,b,d},{c,a,d,b},{c,b,a,d},{c,b,d,a},{c,d,a,b},{c,d,b,a}, 7 {d,a,b,d},{d,a,d,b},{d,b,a,c},{d,b,c,a},{d,c,a,b},{d,c,b,a}, 8 }; 9 for (int i = 0; i < 24; i++) 10 { 11 if (all_cal(all[i][0], all[i][1], all[i][2], all[i][3])) 12 return 1; 13 } 14 return 0; 15 }
主函数调用judge就完成整个算法了✿✿ヽ(°▽°)ノ✿
1 int main() 2 { 3 int a, b, c, d; 4 cin >> a >> b >> c >> d; 5 if (!judge(a, b, c, d)) 6 cout << "凑不成24点" << endl; 7 8 }
失败的话会显示“凑不成24点”
其实这个算法的话我写的可以说基本没有优化,就是枚举所有情况实现的,csdn上有大佬是有更好的思路的,这篇文章也是看了csdn的大佬的代码然后自己修修补补写出来的(我原来看的那篇有bug,大佬自己没发现好像。。。)
就酱
睡觉!