题目描述
"24点"是一种流行的纸牌游戏,适合四名玩家一起玩。
每位玩家有一叠面朝下的牌。每轮游戏中,四位玩家各自翻开自己牌堆的顶牌,让所有人可见。游戏目标是用这四张牌的数值(A=1,J=11,Q=12,K=13
)构造一个算式,使其结果等于 24 24 24。
( ( A ∗ K ) − J ) ∗ Q ((A * K) - J) * Q ((A∗K)−J)∗Q
( ( 1 ∗ 13 ) − 11 ) ∗ 12 ((1 * 13) - 11) * 12 ((1∗13)−11)∗12
例如,题目插图中的示例中,可以构造出一个算式使结果为 24 24 24。
第一个找到这种算式的玩家赢得本轮,并将这四张牌都放到自己牌堆的底部。
每个合法的算式必须:
-
恰好使用这四张牌的数值;
-
只能使用加法、减法、乘法或除法;
-
可以使用括号改变运算顺序;
-
不能将多张牌拼接成多位数(如 2 2 2 和 4 4 4 拼成 24 24 24 是禁止的);
-
除法的结果必须是整数(包括算式中任意子表达式的中间结果也必须为整数)。
在某些情况下,玩家可能很久也找不到等于 24 24 24 的表达式,甚至可能根本不存在这样的表达式。
你的任务是:给定四张牌,找到一个算式,使结果是 不超过 24 24 24 的最大整数。
输入格式
第一行:一个整数 n n n,表示有多少组牌。
接下来每组牌包含 4 4 4 行,每行一个整数,表示一张牌的值。
输出格式
对于每组牌,输出一行一个整数,表示用这 4 4 4 张牌能组合出的、不超过 24 24 24 的最大值。
样例输入
cpp
3
3
3
3
3
1
1
1
1
12
5
13
1
样例输出
cpp
24
4
21
提交链接
思路分析
-
全排列枚举牌的顺序
- 因为牌的排列顺序会影响运算结果(例如 3 3 3 - 5 5 5 和 5 5 5 - 3 3 3 不同)。
- 4 4 4 张牌的所有排列数为 4 ! = 24 4! = 24 4!=24 种。
-
枚举运算符组合
- 4 4 4 张牌有 3 3 3 个空隙,每个空隙可以放
+ - * /
四种运算符。 - 一共有 4 3 = 64 4^3 = 64 43=64 种运算符组合。
- 4 4 4 张牌有 3 3 3 个空隙,每个空隙可以放
-
枚举括号(运算顺序)
- 四个数、三个运算符,合法的运算顺序(括号摆放方式)有 5 5 5 种:
((a op1 b) op2 c) op3 d
(a op1 (b op2 c)) op3 d
a op1 ((b op2 c) op3 d)
a op1 (b op2 (c op3 d))
(a op1 b) op2 (c op3 d)
- 四个数、三个运算符,合法的运算顺序(括号摆放方式)有 5 5 5 种:
-
整数除法检查
- 除法必须整除且不能除以零,运算时要判断。
-
更新最大值
- 如果某个算式结果 ≤ 24 ≤ 24 ≤24 且合法,就用它来更新最大值。
- 全局变量与工具函数
-
vector<char> b{'+', '-', '*', '/'}
- 存四种运算符,方便用下标枚举。
-
int mx
- 记录当前测试用例中最大的不超过 24 24 24 的结果。
-
int cal(int x, char op, int y, bool &ok)
- 执行一次二元运算
x op y
。
ok
用来标记运算是否合法:- 如果是
/
运算,检查除数y
是否为 0; - 检查是否整除
x % y == 0
;
- 如果是
- 如果不满足条件,
ok = false
,表示该运算链无效。
- 执行一次二元运算
- 读取数据
cpp
int t;
cin >> t;
while (t--)
{
vector<int> a(4);
for (int &i : a) cin >> i;
}
- 读取
t
组测试数据。 - 每组数据读入 4 张牌的值。
- 枚举牌的顺序
cpp
sort(a.begin(), a.end());
mx = 0;
do
{
// ...
} while (next_permutation(a.begin(), a.end()));
sort
保证next_permutation
从最小字典序开始枚举,确保不会漏掉排列。next_permutation
会枚举 4 4 4 张牌的所有排列( 24 24 24 种)。- 每次排列会进入一次运算符与括号的枚举。
- 枚举运算符
cpp
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
for (int k = 0; k < 4; k++)
{
char op1 = b[i], op2 = b[j], op3 = b[k];
i, j, k
分别表示三个位置的运算符,取值0~3
,对应+ - * /
。- 一共 64 64 64 种运算符组合。
- 枚举括号方式并计算
- 每种括号方式都要单独用一个
ok = true
,防止上一次计算失败状态影响下一种括号结构。
(1) ((a op1 b) op2 c) op3 d
cpp
ok = true;
r = cal(cal(cal(a[0], op1, a[1], ok), op2, a[2], ok), op3, a[3], ok);
if (ok && r <= 24) mx = max(mx, r);
-
按括号从内到外计算。
-
如果
ok
最终还是true
,表示整个计算合法。
(2) (a op1 (b op2 c)) op3 d
cpp
ok = true;
r = cal(cal(a[0], op1, cal(a[1], op2, a[2], ok), ok), op3, a[3], ok);
if (ok && r <= 24) mx = max(mx, r);
- 先算
(b op2 c)
,再算a op1 (...)
。
(3) a op1 ((b op2 c) op3 d)
cpp
ok = true;
r = cal(a[0], op1, cal(cal(a[1], op2, a[2], ok), op3, a[3], ok), ok);
if (ok && r <= 24) mx = max(mx, r);
- 先算
(b op2 c)
,再(结果 op3 d)
,最后a op1 (...)
。
(4) a op1 (b op2 (c op3 d))
cpp
ok = true;
r = cal(a[0], op1, cal(a[1], op2, cal(a[2], op3, a[3], ok), ok), ok);
if (ok && r <= 24) mx = max(mx, r);
- 先算
(c op3 d)
,再(b op2 结果)
,最后a op1 (...)
。
(5) (a op1 b) op2 (c op3 d)
cpp
ok = true;
r = cal(cal(a[0], op1, a[1], ok), op2, cal(a[2], op3, a[3], ok), ok);
if (ok && r <= 24) mx = max(mx, r);
- 左右两边分别算,再用
op2
连接。
参考代码
cpp
#include <bits/stdc++.h>
using namespace std;
vector<char> b{'+', '-', '*', '/'};
int mx;
int cal(int x, char op, int y, bool &ok)
{
if (op == '+')
return x + y;
else if (op == '-')
return x - y;
else if (op == '*')
return x * y;
else
{
if (y != 0 && x % y == 0)
return x / y;
else
{
ok = false;
return 0;
}
}
}
int main()
{
int t;
cin >> t; // t组样例
while (t--)
{
vector<int> a(4);
for (int &i : a)
cin >> i;
sort(a.begin(), a.end());
mx = 0;
do
{
// 枚举运算符的使用
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
for (int k = 0; k < 4; k++)
{
char op1 = b[i], op2 = b[j], op3 = b[k]; // 三个运算符
bool ok;
int r;
//((a op1 b) op2 c) op3 d
ok = true;
r = cal(cal(cal(a[0], op1, a[1], ok), op2, a[2], ok), op3, a[3], ok);
if (ok && r <= 24)
mx = max(mx, r);
//(a op1 (b op2 c)) op3 d
ok = true;
r = cal(cal(a[0], op1, cal(a[1], op2, a[2], ok), ok), op3, a[3], ok);
if (ok && r <= 24)
mx = max(mx, r);
// a op1 ((b op2 c) op3 d)
ok = true;
r = cal(a[0], op1, cal(cal(a[1], op2, a[2], ok), op3, a[3], ok), ok);
if (ok && r <= 24)
mx = max(mx, r);
// a op1 (b op2 (c op3 d))
ok = true;
r = cal(a[0], op1, cal(a[1], op2, cal(a[2], op3, a[3], ok), ok), ok);
if (ok && r <= 24)
mx = max(mx, r);
//(a op1 b) op2 (c op3 d)
ok = true;
r = cal(cal(a[0], op1, a[1], ok), op2, cal(a[2], op3, a[3], ok), ok);
if (ok && r <= 24)
mx = max(mx, r);
}
}
}
} while (next_permutation(a.begin(), a.end())); // 四个数字的排列
cout << mx << endl;
}
return 0;
}