原理
暴力枚举所有的情况,运算符号4个,加减乘除 + - * / ,整数数字4个。所需要枚举的次数:
- 数字顺序:4个数的全排列,4! = 24。
- 运算符号:4个数需要3个符号,每个可选4种,43 = 64。
- 加括号方式:((AB)C)D、(AB)(CD)、(A(BC))D、A((BC)D)、A(B(CD)),共5种。
- 枚举次数 24*64*5
实现细节
- 全排列枚举由库函数
next_permutation
来完成枚举 - 运算符号由一个整数(范围0—63)表示,
00、01、10、11
分别表示 +-*/,低2位代表运算符1,中2位代表运算符2,高2位代表运算符3。例如37 = 100101
,表示- - *
- 加括号方式手动分别写出来。
- 由于运算过程中含有除法,所以当运算符为除法 且 除数为 0 时,需额外处理一下。
- 验证是否有解和打印解分开,更灵活。
代码
#include <ranges>
#include <string>
#include <iostream>
#include <algorithm>
#include <format>
#include <array>
//运算符 + - * / , 数量 num_n = 4
constexpr double eps = 1e-8;
using array4 = std::array<int, 4>;
// 验证是否有解
bool has_answer(const array4& nums, int op, int method){
bool div_err = false;
auto calc = [&div_err](double x, int oper, double y) -> double {
if(oper == 3 and std::abs(y) < eps){
div_err = true;
return 0;
}
return oper==0 ? x+y : oper==1 ? x-y : oper==2 ? x*y : x/y;
};
auto [a,b,c,d] = nums;
int op1 = op&3, op2 = op>>2&3, op3 = op>>4&3;
double res = 0;
if(method == 1) // ((A+B)+C)+D
res = calc(calc(calc(a, op1, b), op2, c), op3, d);
else if(method == 2) // (A+B)+(C+D)
res = calc(calc(a, op1, b), op2, calc(c, op3, d));
else if(method == 3) // (A+(B+C))+D
res = calc(calc(a, op1, calc(b, op2, c)), op3, d);
else if(method == 4) // A+((B+C)+D)
res = calc(a, op1, calc(calc(b, op2, c), op3, d));
else if(method == 5) // A+(B+(C+D))
res = calc(a, op1, calc(b, op2, calc(c, op3, d)));
return not div_err and std::abs(res - 24) < eps;
}
// 转换为 答案字符串
std::string answer_to_string(const array4& nums, int op, int method){
const char* ops = "+-*/";
const char* res_fmt[] = {
"There are result format string",
"(({}{}{}){}{}){}{} = 24", // ((A+B)+C)+D
"({}{}{}){}({}{}{}) = 24", // (A+B)+(C+D)
"({}{}({}{}{})){}{} = 24", // (A+(B+C))+D
"{}{}(({}{}{}){}{}) = 24", // A+((B+C)+D)
"{}{}({}{}({}{}{})) = 24" // A+(B+(C+D))
};
return std::vformat(res_fmt[method], std::make_format_args(nums[0], ops[op&3], nums[1], ops[op>>2&3], nums[2], ops[op>>4&3], nums[3]));
}
int main() {
array4 nums;
while(true) {
for(int& val : nums){
std::cin >> val;
}
std::ranges::sort(nums);
bool no_solution = true;
do {
for(int method = 1; method <= 5; method++) {
for(int oper = 0; oper < 64; oper++) {
if(has_answer(nums, oper, method)) {
no_solution = false;
std::cout << answer_to_string(nums, oper, method) << std::endl;
//若是打印所有可能答案,注释下方 goto 代码, 注意:重复解未去重
goto outer_loop; // break multiloop
}
}
}
} while(std::ranges::next_permutation(nums).found);
outer_loop:
if(no_solution){
std::cout << "No solution" << std::endl;
}
}
}
测试
每次输入4个数,空格分开。
(date: 2019.2.17【旧】已删除)
(date:2024.8.25【新】)
five years later…
(date:2024.1.15)
递归版的
原理就是遍历每一组的两两组合,组合成一个新数,这样总数量减一,就能按数量递归下去了。
// 24dian.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <string>
using Numbers = std::vector<double>;
using Answers = std::vector<std::string>;
//存储步骤解
Answers answers;
//转为字符串 a + b = c
std::string to_concat(double a, char oper, double b, double c) {
char buffer[100];
std::snprintf(buffer, 100, "%g %c %g = %g", a, oper, b, c);
return std::string(buffer);
}
//递归求解
bool my_solve24(const Numbers& nums, Answers* ans = nullptr) {
int n = nums.size();
//只剩一个数时,直接判断是否等于24
if (n == 1) {
return std::abs(nums[0] - 24) < 1e-6;
}
//第一次,清空答案
if (ans == nullptr) {
ans = &answers;
ans->clear();
}
//尝试两两组合
for (int i = 0;i < n;i++) {
for (int j = i + 1;j < n;j++) {
//拷贝一个不含 nums[i] 和 nums[j] 的新数组
Numbers newArr;
for (int k = 0; k < n; k++) {
if (k == i || k == j)
continue;
newArr.push_back(nums[k]);
}
double a = nums[i], b = nums[j];
//a+b
newArr.push_back(a + b);
if (my_solve24(newArr, ans)) {
ans->push_back(to_concat(a, '+', b, a + b));
return true;
}
newArr.pop_back();
//a-b
newArr.push_back(a - b);
if (my_solve24(newArr, ans)) {
ans->push_back(to_concat(a, '-', b, a - b));
return true;
}
newArr.pop_back();
//b-a
newArr.push_back(b - a);
if (my_solve24(newArr, ans)) {
ans->push_back(to_concat(b, '-', a, b - a));
return true;
}
newArr.pop_back();
//a*b
newArr.push_back(a * b);
if (my_solve24(newArr, ans)) {
ans->push_back(to_concat(a, '*', b, a * b));
return true;
}
newArr.pop_back();
//a/b
if (b != 0) {
newArr.push_back(a / b);
if (my_solve24(newArr, ans)) {
ans->push_back(to_concat(a, '/', b, a / b));
return true;
}
newArr.pop_back();
}
//b/a
if (a != 0) {
newArr.push_back(b / a);
if (my_solve24(newArr, ans)) {
ans->push_back(to_concat(b, '/', a, b / a));
return true;
}
newArr.pop_back();
}
}
}
return false;
}
int main() {
const int n = 4;
while (true) {
Numbers nums;
std::cout << "请输入" << n << "个整数,用空格分隔:" << std::endl;
for (int i = 0; i < n; i++) {
double num;
std::cin >> num;
nums.push_back(num);
}
if (my_solve24(nums)) {
//输出答案
for (auto it = answers.rbegin(); it != answers.rend(); it++) {
std::cout << *it << std::endl;
}
}
else {
std::cout << "无解。" << std::endl;
}
}
return 0;
}