当前位置:首页 > 洛谷 > 洛谷1236题「24点游戏」解题全解析:递归回溯算法与代码实现

洛谷1236题「24点游戏」解题全解析:递归回溯算法与代码实现

2天前

洛谷1236题「24点游戏」解题全解析:递归回溯算法与代码实现 洛谷1236题 24点游戏算法 递归解法 回溯法 代码注释 第1张

一、题目解读

洛谷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题的递归回溯解法,展示了如何将组合优化问题转化为“分治+搜索”的算法模型。作者代码巧妙利用对称运算(交换数字顺序)提升解覆盖率,结合简洁的合法性检查函数,实现了高效解题。对于类似需要枚举所有组合的问题,递归回溯是经典且实用的解决方案,但可进一步优化剪枝策略以提升性能。



原创内容 转载请注明出处

分享给朋友:

相关文章

汉诺塔问题递归解法(C++代码详解) 牛客4414题解题指南

汉诺塔问题递归解法(C++代码详解) 牛客4414题解题指南

一、题目解读汉诺塔问题是一个经典的递归算法题目:有n个大小不同的圆盘按从大到小顺序放在柱A上,需借助柱B,将圆盘移至柱C,每次只能移动一个圆盘,且大圆盘不能置于小圆盘之上。牛客4414题要求编写函数输...

快速排序算法详解 C++代码实现

一、简介和特点快速排序(QuickSort)是一种经典的高效排序算法,采用分治思想:通过选定一个枢轴元素(pivot),将数组划分为左右两部分,使左侧元素均小于枢轴,右侧元素均大于枢轴,再递归对子数组...

手搓邻接矩阵类代码注释与实现指南:从零开始理解图论数据结构(适合小白)

一、简介和特点邻接矩阵是用于表示图(Graph)的一种数据结构,通常用二维数组存储节点之间的关系。本文的graph类通过C++实现了一个基础的邻接矩阵结构,特点如下:1. 动态创建:根据用户输入的节点...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。