洛谷1236题「24点游戏」解题全解析:递归回溯算法与代码实现
一、题目解读
洛谷1236题要求玩家通过给定的四个整数(范围1-13),使用加减乘除四种运算(允许括号),计算出结果24。题目需输出所有可能的运算步骤,若不存在解则输出“NO SOLUTION”。这本质是一个组合优化问题,需通过算法搜索所有可能的运算组合并验证合法性。
二、解题思路
作者采用递归回溯算法解决此问题。核心思想是将四个数逐步合并为单个结果,过程中尝试所有运算组合,并利用标记(found)避免重复计算。算法通过双循环枚举两两数字的组合,再调用自定义运算函数(compute)检查合法性,最终递归调用求解剩余数字,符合“分治+回溯”的经典模式。
三、解题步骤详解
1. 初始化:使用vector<int> nums存储原始数字,vector<string> steps记录步骤,found标记初始化为false。
2. 递归求解函数solve(vector<int>& nums):
若仅剩一个数字且为24,标记found并终止递归。
双循环遍历所有数字对(i, j),生成剩余数字集合next(排除i,j)。
对每个运算符(+, -, *, /):
调用compute(a, b, op)计算结果,若非法(如除数为0或非整数)跳过。
生成步骤字符串(如"3+5=8"),加入steps并扩展next集合。
递归调用solve(next),若找到解则立即返回。
回溯:弹出步骤和结果数字,恢复状态。
关键优化:交换(a, b)顺序再次尝试运算,扩大搜索范围,避免遗漏对称情况。
3. 输出结果:根据found状态输出步骤或“NO SOLUTION”。
四、代码与注释
#include <iostream> #include <vector> #include <algorithm> using namespace std; vector<string> steps; // 存储运算步骤 bool found = false; // 标记是否找到解 // 执行运算并检查合法性 int compute(int a, int b, char op) { switch(op) { case '+': return a + b; case '-': return a > b? a - b : b - a; // 确保结果非负 case '*': return a * b; case '/': if(b == 0 || (a % b!= 0)) return -1; // 除法非法条件 return a / b; } return -1; } // 生成运算步骤字符串 string formatStep(int a, int b, char op, int res) { if(a < b) swap(a, b); // 统一格式(大数在前) return to_string(a) + string(1,op) + to_string(b) + "=" + to_string(res); } // 递归求解24点 void solve(vector<int>& nums) { if(found) return; // 剪枝:找到解后终止 if(nums.size() == 1) { if(nums[0] == 24) found = true; // 目标达成 return; } for(int i = 0; i < nums.size(); i++) { for(int j = i+1; j < nums.size(); j++) { vector<int> next; // 存储剩余数字 // 保留未使用的数字 for(int k = 0; k < nums.size(); k++) if(k!= i && k!= j) next.push_back(nums[k]); // 尝试四种运算 int a = nums[i], b = nums[j]; char ops[] = {'+', '-', '*', '/'}; for(char op : ops) { int res = compute(a, b, op); if(res <= 0) continue; // 跳过非法结果 string step = formatStep(a, b, op, res); steps.push_back(step); next.push_back(res); solve(next); // 递归求解剩余数字 if(found) return; // 找到解则回溯跳出 // 回溯:撤销当前步骤 steps.pop_back(); next.pop_back(); } // 交换a,b顺序再尝试(如3+5与5+3) for(char op : ops) { int res = compute(b, a, op); if(res <= 0) continue; string step = formatStep(b, a, op, res); steps.push_back(step); next.push_back(res); solve(next); if(found) return; steps.pop_back(); next.pop_back(); } } } } int main() { vector<int> nums(4); for(int i = 0; i < 4; i++) cin >> nums[i]; solve(nums); if(found && steps.size() == 3) { for(string s : steps) cout << s << endl; } else { cout << "No answer!" << endl; } return 0; }
五、总结
本文通过分析洛谷1236题的递归回溯解法,展示了如何将组合优化问题转化为“分治+搜索”的算法模型。作者代码巧妙利用对称运算(交换数字顺序)提升解覆盖率,结合简洁的合法性检查函数,实现了高效解题。对于类似需要枚举所有组合的问题,递归回溯是经典且实用的解决方案,但可进一步优化剪枝策略以提升性能。
原创内容 转载请注明出处