赞
踩
问题描述:
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
前言:
参考解法:
原博客讲得很详细,评论区也有不少问题解答。
(仅需解法的 同学/读者 直接跳转 即可)
这里我还是只讲一下我的解题过程(主要是错误的原因)。
临近省赛,这篇文章就再小小总结一下目前做过的《算法训练》题目。
(删了一大段感慨的话)
笔者,也是要去刷一刷官网的真题才行……
开始:
题做到这里,相信大家已经对一些算法(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%。
上网查了以后发现存在优先级和分配律的问题,
按自己的方式,笔者写的检查错误的代码如下:
- #include<iostream>
- using namespace std;
-
- //分组数量
- int N = 0;
-
- //数据:最多5组,每组4个
- int Date[5][4] = { 0 };
-
- //标记数据状态:false:未被使用;true:已使用
- int Date_index[5][4] = { 0 };
-
- int result[6] = { 0 };
-
- //检查数据是否"全部"都已使用 - 作为边界条件
- bool func01(int row)
- {
- for (int i = 0; i < 4; i++)
- {
- if (!Date_index[row][i])
- return false;
- }
- return true;
- }
-
- //将数字转"字符"返回
- string func02(int sum)
- {
- string s = "";
- do
- {
- s += '0' + sum % 10;
- sum /= 10;
- } while (sum > 0);
- reverse(s.begin(), s.end());
- return s;
- }
-
- string S;
-
- void func(const int row, int sum = 0, bool bol = false, string s = "", int x = 0)
- {
- x++;
- //若结果已为最大值,无需继续
- if (result[row] == 24)
- return;
-
- //该row组数据全部用上 - (更新)结束
- if (func01(row))
- {
- s += " = " + func02(sum);
- if (result[row] < sum && sum <= 24)
- {
- result[row] = sum;
- S = s;
- }
- return;
- }
-
- //第一次调用时,填充任一数字作为第一位(防止存在0 * 0导致莫名去除了一些数据)
- if (!bol)
- {
- for (int i = 0; i < 4; i++)
- {
- sum = Date[row][i];
- Date_index[row][i] = x;//标记已使用
- s = func02(Date[row][i]);
- func(row, sum, true, s, x);
- Date_index[row][i] = 0;//回溯
- }
- }
- //第二/三/四次调用
- else
- {
- for (int i = 0; i < 4; i++)
- {
- if (!Date_index[row][i])
- {
- Date_index[row][i] = x;//标记已使用
-
- string temp;
-
- int x1 = sum + Date[row][i];//加
- temp = s + " + " + func02(Date[row][i]);
- func(row, x1, true, temp, x);
-
- int x2 = sum - Date[row][i];//减
- temp = "|" + s + " - " + func02(Date[row][i]) + "|";
- x2 = x2 > 0 ? x2 : -x2;
- func(row, x2, true, temp, x);
-
- int x3 = sum * Date[row][i];//乘
- temp = "(" + s + ")" + " * " + func02(Date[row][i]);
- func(row, x3, true, temp, x);
-
- if (Date[row][i] != 0 && sum % Date[row][i] == 0)
- {
- temp = "(" + s + ")" + " / " + func02(Date[row][i]);
- func(row, sum / Date[row][i], true, temp, x);//除
- }
- else if (sum != 0 && Date[row][i] % sum == 0)
- {
- temp = func02(Date[row][i]) + " / " + "(" + s + ")";
- func(row, Date[row][i] / sum, true, temp, x);//除
- }
- Date_index[row][i] = 0;//回溯
- }
- }
- }
- }
-
- int n = 1;//n组测试数据
- int nums[4];//4个数字
- int ans;//不超过24的最大值
-
- //函数功能: 在有n个数的数组nums中 寻找最大的不超过24的值。
- void f(int nums[], int n) {
- //只有1个数时,比较最大值
- if (n == 1) {
- //如果该数不大于24,更新最大值
- if (nums[n - 1] <= 24) {
- ans = ans < nums[n - 1] ? nums[n - 1] : ans;
- }
- return;
- }
-
- //遍历两个数的组合
- for (int i = 0; i < n - 1; i++) {
- for (int j = i + 1; j < n; j++) {
- int a = nums[i], b = nums[j];
-
- nums[j] = a + b; //加法和乘法具有交换律
- nums[i] = nums[n - 1];
- f(nums, n - 1);
-
- nums[j] = a * b;
- nums[i] = nums[n - 1];
- f(nums, n - 1);
-
- nums[j] = a - b; //减法和除法不具有交换律
- nums[i] = nums[n - 1];
- f(nums, n - 1);
-
- nums[j] = b - a;
- nums[i] = nums[n - 1];
- f(nums, n - 1);
-
- if (b != 0 && a % b == 0) { //除数不能为0
- nums[j] = a / b;
- nums[i] = nums[n - 1];
- f(nums, n - 1);
- }
-
- if (a != 0 && b % a == 0) {
- nums[j] = b / a;
- nums[i] = nums[n - 1];
- f(nums, n - 1);
- }
-
- nums[i] = a; //恢复现场
- nums[j] = b;
- }
- }
- }
-
- int main()
- {
- 输入数据
- //cin >> N;
- //for (int i = 0; i < N; i++)
- // for (int j = 0; j < 4; j++)
- // {
- // cin >> Date[i][j];
- // }
- 对每层进行运算
- //for (int i = 0; i < N; i++)
- // func(i);
- 输出结果
- //for (int i = 0; i < N; i++)
- // cout
- // << S << endl
- // //<< result[i] << endl
- // ;
-
- int cnt = 0;
- for (int a = 1; a <= 13; a++)
- {
- for (int b = a; b <= 13; b++)
- {
- for (int c = b; c <= 13; c++)
- {
- for (int d = c; d <= 13; d++)
- {
- Date[0][0] = a;
- Date[0][1] = b;
- Date[0][2] = c;
- Date[0][3] = d;
- func(0);
- cout << S << endl;
-
- ans = 0;
- nums[0] = a;
- nums[1] = b;
- nums[2] = c;
- nums[3] = d;
- f(nums, 4);
- cout << " -> " << ans << endl;
-
- if (ans != result[0])
- system("pause>nul");
-
- S = "";
- result[0] = 0;
- cnt++;
-
- }
- }
- }
- }
- cout << cnt << endl;
-
- return 0;
- }

部分运行截图如下:
每次都会在 = 结果和 -> 结果 不同的时候暂停。
肉眼可得:若按最朴素的黄色dfs,对:1 1 1 9 得到的结果只有(1+1)*9+1=19最大。
但我们仍易得出:最大应该是(1 + 1) * (1 + 9) = 20,
可是原先的dfs思路是无法做到:将前后两个各自相加的。只能以(之前得到的数)一个数为基准,然后对其不断 加减乘除……
所以又需"升级"一下dfs解法。
具体解法看上面提到的博客,我就不详写了。
结束:
最近实在比较忙,还得准备省赛,抽空写文章小小总结一下dfs,分享 + 加深 自己的理解。
……
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。