C++ 实现计算24点

原理

暴力枚举所有的情况,运算符号4个,加减乘除 + - * / ,整数数字4个。所需要枚举的次数:

  1. 数字顺序:4个数的全排列,4! = 24
  2. 运算符号:4个数需要3个符号,每个可选4种,43 = 64
  3. 加括号方式:((AB)C)D(AB)(CD)(A(BC))DA((BC)D)A(B(CD)),共5种。
  4. 枚举次数 24*64*5

实现细节

  • 全排列枚举由库函数 next_permutation来完成枚举
  • 运算符号由一个整数(范围0—63)表示,00、01、10、11分别表示 +-*/,低2位代表运算符1,中2位代表运算符2,高2位代表运算符3。例如 37 = 100101,表示 - - *
  • 加括号方式手动分别写出来。
  • 由于运算过程中含有除法,所以当运算符为除法 且 除数为 0 时,需额外处理一下。
  • 验证是否有解和打印解分开,更灵活。

代码

#include <iostream>
#include <algorithm>
//运算 + - * / , 数量 num_n = 4
const int num_n = 4, max_oper = 64;
// 验证是否有解
bool has_answer(int* arr, int oper_code, int method){
	bool no_div_err = true;
	auto calc = [&no_div_err](double a, double b, int op) -> double {
		if(b == 0 && op == 3){
			no_div_err = false;
			return 0;
		}
		return op==0 ? a+b : op==1 ? a-b : op==2 ? a*b : a/b;
	};
	int o1 = oper_code % 4, o2 = (oper_code >> 2) % 4, o3 = (oper_code >> 4) % 4;
	if(method == 1){	// ((AB)C)D
		return no_div_err && std::abs(calc(calc(calc(arr[0], arr[1], o1), arr[2], o2), arr[3], o3) - 24) < 1e-6;
	}else if(method == 2){ // (AB)(CD)
		return no_div_err && std::abs(calc(calc(arr[0], arr[1], o1), calc(arr[2], arr[3], o3), o2) - 24) < 1e-6;
	}else if(method == 3){ // (A(BC))D
		return no_div_err && std::abs(calc(calc(arr[0], calc(arr[1], arr[2], o2), o1), arr[3], o3) - 24) < 1e-6;
	}else if(method == 4){ // A((BC)D)
		return no_div_err && std::abs(calc(arr[0], calc(calc(arr[1], arr[2], o2), arr[3], o3), o1) - 24) < 1e-6;
	}else if(method == 5){ // A(B(CD))
		return no_div_err && std::abs(calc(arr[0], calc(arr[1], calc(arr[2], arr[3], o3), o2), o1) - 24) < 1e-6;
	}
	return false;
}
// 转换为 答案字符串 
std::string answer_to_string(int* arr, int oper_code, int method){
	const char* op_str = "+-*/";
	std::string a = std::to_string(arr[0]), b = std::to_string(arr[1]), c = std::to_string(arr[2]), d = std::to_string(arr[3]);
	char o1 = op_str[oper_code%4], o2 = op_str[(oper_code>>2)%4], o3 = op_str[(oper_code>>4)%4];
	if(method == 1){// ((AB)C)D
		return "((" + a + o1 + b + ")" + o2 + c + ")" + o3 + d + " = 24";
	}else if(method == 2){// (AB)(CD)
		return "(" + a + o1 + b + ")" + o2 + "(" + c + o3 + d + ") = 24";
	}else if(method == 3){// (A(BC))D
		return "(" + a + o1 + "(" + b + o2 + c + "))" + o3 + d + " = 24";
	}else if(method == 4){ // A((BC)D)
		return a + o1 + "((" + b + o2 + c + ")" + o3 + d + ")" + " = 24";
	}else if(method == 5){// A(B(CD))
		return a + o1 + "(" + b + o2 + "(" + c + o3 + d + "))" + " = 24";
	}
	return std::string("error occurred");
}
int main() {
	int arr[num_n]; // 4 个数字
	outer_loop:
	while(true) {
		for(int i = 0; i < num_n; i++) {
			std::cin >> arr[i];
		}
		std::sort(arr, arr + num_n);
		bool result = false;
		do {			
			for(int meth = 1; meth <= 5; meth++) {
				for(int oper = 0; oper < max_oper; oper++) {
					result = has_answer(arr, oper, meth);
					if(result) {
						std::cout << answer_to_string(arr, oper, meth) << std::endl;
						//若是打印所有可能答案,注释下方 goto 代码
						goto outer_loop; // break multiloop
					}
				}
			}
		} while(std::next_permutation(arr, arr + num_n));
		if(!result)
			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;
}

测试

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值