一、题目描述
给你四个数字 A 1 , A 2 , A 3 , A 4 A_1,A_2,A_3,A_4 A1,A2,A3,A4 ,请使用加、减、乘、除和括号来实现 24 24 24 点问题的计算。
规则如下:
- 每个数字只能使用一次。
- 若需要使用除法,则过程中不能出现小数。例如: ( 2 × 2 ) ÷ 4 + 2 (2 \times 2) \div 4 + 2 (2×2)÷4+2 是一个合法的表达式,但 2 ÷ 4 × 2 2 \div 4 \times 2 2÷4×2 则不是一个合法的表达式,即使它的最终计算结果是一个整数。
- 最终的计算结果应该是 24 24 24 ,输出字典序最小的操作序列。
数据范围:
1
≤
A
i
≤
9
(
1
≤
i
≤
4
)
1 \le A_i \le 9(1 \le i \le 4)
1≤Ai≤9(1≤i≤4) 。
一道除了输出不同但类似的题:算
24
24
24 点
二、算法原理
由于本题的计算只涉及到 4 4 4 个数字 A 1 , A 2 , A 3 , A 4 A_1,A_2,A_3,A_4 A1,A2,A3,A4 所以可以使用 dfs 算法进行计算。
1. 搜索框架
搜索对象:数字
A
1
,
A
2
,
A
3
,
A
4
A_1,A_2,A_3,A_4
A1,A2,A3,A4
搜索内容:使用运算符
+
,
−
,
×
,
÷
,
(
)
+,-,\times,\div,()
+,−,×,÷,() 连接四个数字
2. 具体的搜索实现方法
(1) 需要包含的头文件:
#include <iostream>
#include <string>
//或者使用下面这个,注意命名重复的问题,不要使用 map, x1, x2 等变量名
#include <bits/stdc++.h>
提醒:在比赛的时候,尽量不要使用 bits/stdc++.h 这个库,否则可能会出现各种意想不到的问题。由于比赛评测机是 Linux 环境,编译规则与 Windows 有所不同。
(2) 定义数组和变量:
int a[8]; //用来存储输入的数字
int v[8]; //用来在搜索过程中记录每个数字是否被使用过,避免重复搜索
string s[8]; //记录运算过程中产生的表达式,其中的每个 string 记录这一次运算后的整个表达式
string ans; //用来存储字典序最小的答案
(3) 搜索过程的文字描述:
首先,我们直接从输入数组的末端(
4
4
4 号位置)开始搜索。设已经搜索到了第
p
p
p 号位置,则每次搜索的过程,枚举从第
1
1
1 到第
p
p
p 个位置的元素中未参与运算的两个元素。这里,
p
p
p 可以大于
4
4
4 ,此时
A
p
A_p
Ap 表示每一次运算后的结果。如果找到了这样的两个元素,记这两个元素在集合
A
A
A 中的位置分别为
i
,
j
i,j
i,j 。枚举可以对
(
A
i
,
A
j
)
(A_i,A_j)
(Ai,Aj) 数对进行的四则运算。
然后,对于每个运算,采用如下的处理方式:
标记位置
i
,
j
i,j
i,j 为使用过,令新位置
A
p
+
1
A_{p+1}
Ap+1 的值为
A
i
op
A
j
(
op
∈
{
+
,
−
,
×
,
÷
}
)
A_i \text{ op } A_j \text{ } (\text{op} \in \{+,-,\times,\div\})
Ai op Aj (op∈{+,−,×,÷}) ,令
s
p
+
1
s_{p+1}
sp+1 的值为 "(" + s[i] + op + s[j] + ")" ,
op
∈
{
+
,
−
,
×
,
÷
}
\text{op} \in \{+,-,\times,\div\}
op∈{+,−,×,÷} 。在使用除法时特判:如果能被整除,则进行上述的操作。注意除数可能是
0
0
0 !
最后,我们需要判断是否到达递归出口。如果
p
p
p 的值是
7
7
7 ,因为
4
4
4 个数之间只能进行
3
3
3 次运算,所以到达递归出口,判断
A
p
A_p
Ap ,也就是最后的运算结果是否是
24
24
24 。如果是,则满足记录答案的条件 ,用记录答案的字符串 ans 记录当前的操作序列。注意, ans 在非空的状态下需要与
S
p
S_p
Sp 进行取最小值的运算来获得字典序最小的答案。
(4) 搜索过程的代码实现:
void dfs(int p) //p的含义如上
{
if (p == 7) //完成全部运算,到达递归出口
{
if (a[p] == 24) //最终的运算结果是 24
{
if (ans == "") //变量 ans 中尚未记录答案
{
ans = s[p]; // ans 为当前的操作序列
}
else
{
ans = min(ans, s[p]); //取字典序最小,字符串支持 < 操作
}
}
return ; //返回
}
for (int i = 1; i <= p; ++i) //枚举第一个操作数
{
if (v[i]) continue; //如果被使用过,则直接退出
v[i] = 1; //标记为使用过
for (int j = 1; j <= p; ++j) //枚举第二个操作数
{
if (v[j]) continue; //第二个数不能被使用过
if (i == j) continue; //两个数不能相同
v[j] = 1; //标记为使用过
//枚举四种运算
//加法
a[p + 1] = a[i] + a[j];
s[p + 1] = "(" + s[i] + "+" + s[j] + ")";
dfs(p + 1);
//减法
a[p + 1] = a[i] - a[j];
s[p + 1] = "(" + s[i] + "-" + s[j] + ")";
dfs(p + 1);
//乘法
a[p + 1] = a[i] * a[j];
s[p + 1] = "(" + s[i] + "*" + s[j] + ")";
dfs(p + 1);
//除法,特判
if (a[j] != 0 && a[i] % a[j] == 0)
{
a[p + 1] = a[i] / a[j];
s[p + 1] = "(" + s[i] + "/" + s[j] + ")";
dfs(p + 1);
}
v[j] = 0; //回溯
}
v[i] = 0; //回溯
}
}
(5) 搜索过程的图片解析

三、C++ 语言代码实现及细节
注意 s 数组要初始化成 a 数组的字符形式即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 10;
int a[N], v[N];
string s[N], ans;
void dfs(int p)
{
if (p == 7)
{
if (a[p] == 24)
{
if (ans == "")
{
ans = s[p];
}
else
{
ans = min(ans, s[p]);
}
}
}
for (int i = 1; i <= p; ++i)
{
if (v[i]) continue;
v[i] = 1;
for (int j = 1; j <= p; ++j)
{
if (i == j) continue;
if (v[j]) continue;
v[j] = 1;
a[p + 1] = a[i] + a[j];
s[p + 1] = "(" + s[i] + "+" + s[j] + ")";
dfs(p + 1);
a[p + 1] = a[i] - a[j];
s[p + 1] = "(" + s[i] + "-" + s[j] + ")";
dfs(p + 1);
a[p + 1] = a[i] * a[j];
s[p + 1] = "(" + s[i] + "*" + s[j] + ")";
dfs(p + 1);
if (a[j] != 0 && a[i] % a[j] == 0)
{
a[p + 1] = a[i] / a[j];
s[p + 1] = "(" + s[i] + "/" + s[j] + ")";
dfs(p + 1);
}
v[j] = 0;
}
v[i] = 0;
}
}
int main()
{
for (int i = 1; i <= 4; ++i)
{
cin >> a[i];
s[i] = (char)(a[i] + '0');
}
dfs(4);
cout << ans << endl;
return 0;
}
7465

被折叠的 条评论
为什么被折叠?



