当前位置:   article > 正文

蓝桥杯 试题 算法训练 24点 C++ 详解

试题 算法训练 24点

问题描述:

  24点游戏是一个非常有意思的游戏,很流行,玩法很简单:给你4张牌,每张牌上有数字(其中A代表1,J代表11,Q代表12,K代表13),你可以利用数学中的加、减、乘、除以及括号想办法得到24,例如:
  ((A*K)-J)*Q等价于((1*13)-11)*12=24
  加减乘不用多说了,但除法必须满足能整除才能除!这样有一些是得不到24点的,所以这里只要求求出不超过24的最大值。

输入格式:

  输入第一行N(1<=N<=5)表示有N组测试数据。每组测试数据输入4行,每行一个整数(1到13)表示牌值。

输出格式:

  每组测试数据输出一个整数,表示所能得到的最大的不超过24的值。

样例输入:

3
3
3
3
3
1
1
1
1
12
5
13
1

样例输出:

24
4
21


前言:

参考解法:

蓝桥杯 算法训练 24点_陆小路-1的博客-CSDN博客

原博客讲得很详细,评论区也有不少问题解答。

(仅需解法的 同学/读者 直接跳转 即可)

这里我还是只讲一下我的解题过程(主要是错误的原因)。

临近省赛,这篇文章就再小小总结一下目前做过的《算法训练》题目。

(删了一大段感慨的话)

笔者,也是要去刷一刷官网的真题才行……


开始:

题做到这里,相信大家已经对一些算法(dp,dfs)有了一定的了解。

已经做过的题目:

《印章》:dp(动态规划),最终结果由上一步结果推导出。

《拿金币》:同上。

《无聊的逗》:经典的dfs算法。

《礼物》:找规律。

《跳马》:dfs(深度优先搜索), bfs(宽度优先搜索) 都能解。

《数的潜能》:找规律。

《娜神平衡》:dfs + dp?

《粘木棍》:dfs

《车的放置》:dfs

《24点》:dfs

其中,《印章》《拿金币》《礼物》《数的潜能》就不讲了,随机应变吧。

笔者浅浅总结一下这些题所用dfs的区别。

《无聊的逗》,经典的dfs,利用每层递归的三种操作,遍历出所有的情况。但仅限于对当前"对象"进行操作。

《跳马》,经典的dfs,利用每层递归的两种操作,遍历出所有的情况。但也仅限于对当前"对象"进行操作。

《娜神平衡》,同为dfs操作,但它却可以实现:对一些对象进行选择(每层内部都有一个for循环遍历所有对象)。不难发现,其依据是用int或bool数组存放对象的状态

《粘木棍》,……

《车的放置》,……

《24点》,而本题的dfs操作又有不同,它是每层循环都挑出两个数,对齐进行(加减乘除)。


笔者水平有限,对这些dfs的总结为:

橙色dfs的作用:对某个对象进行不同的操作,适用于穷举"按顺序"的所有情况。

例:给定ABCDEFG……不同的物品,每个物品都可以拿或不拿,请穷举所有情况。

黄色dfs的作用:对某个对象进行不同的操作,适用于穷举"不按顺序"的所有情况。

例:给定ABCDEFG……不同的物品,请穷举所有组合(ABCDEGF……)。

绿色dfs的作用:对两个对象进行不同的操作,适用于穷举"两两配对?"的所有情况。

其实跟黄色dfs差不多,,,


接下来看题,其本质就是问:对4个数进行加减乘除的操作,怎么得到小于等于24最大的数?

一开始我是延续上几题《娜神平衡》dfs想法,但提交程序后正确率最高只有60%。

上网查了以后发现存在优先级和分配律的问题,

按自己的方式,笔者写的检查错误的代码如下:

  1. #include<iostream>
  2. using namespace std;
  3. //分组数量
  4. int N = 0;
  5. //数据:最多5组,每组4个
  6. int Date[5][4] = { 0 };
  7. //标记数据状态:false:未被使用;true:已使用
  8. int Date_index[5][4] = { 0 };
  9. int result[6] = { 0 };
  10. //检查数据是否"全部"都已使用 - 作为边界条件
  11. bool func01(int row)
  12. {
  13. for (int i = 0; i < 4; i++)
  14. {
  15. if (!Date_index[row][i])
  16. return false;
  17. }
  18. return true;
  19. }
  20. //将数字转"字符"返回
  21. string func02(int sum)
  22. {
  23. string s = "";
  24. do
  25. {
  26. s += '0' + sum % 10;
  27. sum /= 10;
  28. } while (sum > 0);
  29. reverse(s.begin(), s.end());
  30. return s;
  31. }
  32. string S;
  33. void func(const int row, int sum = 0, bool bol = false, string s = "", int x = 0)
  34. {
  35. x++;
  36. //若结果已为最大值,无需继续
  37. if (result[row] == 24)
  38. return;
  39. //该row组数据全部用上 - (更新)结束
  40. if (func01(row))
  41. {
  42. s += " = " + func02(sum);
  43. if (result[row] < sum && sum <= 24)
  44. {
  45. result[row] = sum;
  46. S = s;
  47. }
  48. return;
  49. }
  50. //第一次调用时,填充任一数字作为第一位(防止存在0 * 0导致莫名去除了一些数据)
  51. if (!bol)
  52. {
  53. for (int i = 0; i < 4; i++)
  54. {
  55. sum = Date[row][i];
  56. Date_index[row][i] = x;//标记已使用
  57. s = func02(Date[row][i]);
  58. func(row, sum, true, s, x);
  59. Date_index[row][i] = 0;//回溯
  60. }
  61. }
  62. //第二/三/四次调用
  63. else
  64. {
  65. for (int i = 0; i < 4; i++)
  66. {
  67. if (!Date_index[row][i])
  68. {
  69. Date_index[row][i] = x;//标记已使用
  70. string temp;
  71. int x1 = sum + Date[row][i];//加
  72. temp = s + " + " + func02(Date[row][i]);
  73. func(row, x1, true, temp, x);
  74. int x2 = sum - Date[row][i];//减
  75. temp = "|" + s + " - " + func02(Date[row][i]) + "|";
  76. x2 = x2 > 0 ? x2 : -x2;
  77. func(row, x2, true, temp, x);
  78. int x3 = sum * Date[row][i];//乘
  79. temp = "(" + s + ")" + " * " + func02(Date[row][i]);
  80. func(row, x3, true, temp, x);
  81. if (Date[row][i] != 0 && sum % Date[row][i] == 0)
  82. {
  83. temp = "(" + s + ")" + " / " + func02(Date[row][i]);
  84. func(row, sum / Date[row][i], true, temp, x);//除
  85. }
  86. else if (sum != 0 && Date[row][i] % sum == 0)
  87. {
  88. temp = func02(Date[row][i]) + " / " + "(" + s + ")";
  89. func(row, Date[row][i] / sum, true, temp, x);//除
  90. }
  91. Date_index[row][i] = 0;//回溯
  92. }
  93. }
  94. }
  95. }
  96. int n = 1;//n组测试数据
  97. int nums[4];//4个数字
  98. int ans;//不超过24的最大值
  99. //函数功能: 在有n个数的数组nums中 寻找最大的不超过24的值。
  100. void f(int nums[], int n) {
  101. //只有1个数时,比较最大值
  102. if (n == 1) {
  103. //如果该数不大于24,更新最大值
  104. if (nums[n - 1] <= 24) {
  105. ans = ans < nums[n - 1] ? nums[n - 1] : ans;
  106. }
  107. return;
  108. }
  109. //遍历两个数的组合
  110. for (int i = 0; i < n - 1; i++) {
  111. for (int j = i + 1; j < n; j++) {
  112. int a = nums[i], b = nums[j];
  113. nums[j] = a + b; //加法和乘法具有交换律
  114. nums[i] = nums[n - 1];
  115. f(nums, n - 1);
  116. nums[j] = a * b;
  117. nums[i] = nums[n - 1];
  118. f(nums, n - 1);
  119. nums[j] = a - b; //减法和除法不具有交换律
  120. nums[i] = nums[n - 1];
  121. f(nums, n - 1);
  122. nums[j] = b - a;
  123. nums[i] = nums[n - 1];
  124. f(nums, n - 1);
  125. if (b != 0 && a % b == 0) { //除数不能为0
  126. nums[j] = a / b;
  127. nums[i] = nums[n - 1];
  128. f(nums, n - 1);
  129. }
  130. if (a != 0 && b % a == 0) {
  131. nums[j] = b / a;
  132. nums[i] = nums[n - 1];
  133. f(nums, n - 1);
  134. }
  135. nums[i] = a; //恢复现场
  136. nums[j] = b;
  137. }
  138. }
  139. }
  140. int main()
  141. {
  142. 输入数据
  143. //cin >> N;
  144. //for (int i = 0; i < N; i++)
  145. // for (int j = 0; j < 4; j++)
  146. // {
  147. // cin >> Date[i][j];
  148. // }
  149. 对每层进行运算
  150. //for (int i = 0; i < N; i++)
  151. // func(i);
  152. 输出结果
  153. //for (int i = 0; i < N; i++)
  154. // cout
  155. // << S << endl
  156. // //<< result[i] << endl
  157. // ;
  158. int cnt = 0;
  159. for (int a = 1; a <= 13; a++)
  160. {
  161. for (int b = a; b <= 13; b++)
  162. {
  163. for (int c = b; c <= 13; c++)
  164. {
  165. for (int d = c; d <= 13; d++)
  166. {
  167. Date[0][0] = a;
  168. Date[0][1] = b;
  169. Date[0][2] = c;
  170. Date[0][3] = d;
  171. func(0);
  172. cout << S << endl;
  173. ans = 0;
  174. nums[0] = a;
  175. nums[1] = b;
  176. nums[2] = c;
  177. nums[3] = d;
  178. f(nums, 4);
  179. cout << " -> " << ans << endl;
  180. if (ans != result[0])
  181. system("pause>nul");
  182. S = "";
  183. result[0] = 0;
  184. cnt++;
  185. }
  186. }
  187. }
  188. }
  189. cout << cnt << endl;
  190. return 0;
  191. }

部分运行截图如下:

每次都会在 = 结果 -> 结果 不同的时候暂停。

肉眼可得:若按最朴素的黄色dfs,对:1 1 1 9 得到的结果只有(1+1)*9+1=19最大。

但我们仍易得出:最大应该是(1 + 1) * (1 + 9) = 20,

可是原先的dfs思路是无法做到:将前后两个各自相加的。只能以(之前得到的数)一个数为基准,然后对其不断 加减乘除……

所以又需"升级"一下dfs解法。

具体解法看上面提到的博客,我就不详写了。


结束:

最近实在比较忙,还得准备省赛,抽空写文章小小总结一下dfs,分享 + 加深  自己的理解。

……

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/64307
推荐阅读
相关标签
  

闽ICP备14008679号