题目的主要信息:

完成24点运算,输入四个数字,找到合适的运算顺序,使得结果为24。需要注意以下几点:

  • 如果有大小王输出ERROR表示无法计算。
  • 仅使用+、-、*、/ 四种运算。
  • 最后如果没有找到运算顺序,则输出NONE。

方法一:暴力递归

首先将给定的四个数字提取出来,如果有JQKA则转换为数字。如果有joker或者JOKER则输出ERROR。然后对四个数字的所有排列枚举一遍,每个排列都判断是否能完成24点计算,如果能,则结束循环,输出运算。判断是否能完成24点也是用暴力枚举的方式,首先枚举第一个运算符,然后再枚举第二个运算符,枚举第三个运算符,如果当前运算符的选择可以的到结果24则结束递归。输出的时候需要注意把11、12、13、1数字转为J、Q、K、A。 alt 具体做法:

#include <bits/stdc++.h>
using namespace std;
map<char, int> mp;

bool get(int a, int b, char& op1) {//穷举最后一个符号
    if (a + b == 24) {
        op1 = '+';
        return true; 
    }
    if (a - b == 24) { 
        op1 = '-'; 
        return true; 
    }
    if (a * b == 24) {
        op1 = '*'; 
        return true; 
    }
    if (a / b == 24) { 
        op1 = '/'; 
        return true; 
    }
    return false;
}

bool get(int a, int b, int c, char& op1, char& op2) {//穷举第二个运算符
    if (get(a + b, c, op2)) {
        op1 = '+';
        return true; 
    }
    if (get(a - b, c, op2)) {
        op1 = '-'; 
        return true; 
    }
    if (get(a * b, c, op2)) { 
        op1 = '*';
        return true;
    }
    if (get(a / b, c, op2)) { 
        op1 = '/'; 
        return true;
    }
    return false;
}

bool get(int a, int b, int c, int d, char& op1, char& op2, char& op3) {//穷举第一个符号
    //+ - * /每一种方法都穷举一遍,递归计算
    if (get(a + b, c, d, op2, op3)) { 
        op1 = '+'; 
        return true; 
    }
    if (get(a - b, c, d, op2, op3)) { 
        op1 = '-'; 
        return true; 
    }
    if (get(a * b, c, d, op2, op3)) { 
        op1 = '*'; 
        return true; 
    }
    if (get(a / b, c, d, op2, op3)) { 
        op1 = '/';
        return true;
    }
    return false;
}

int main() {
    string s;
    mp['J'] = 11, mp['Q'] = 12, mp['K'] = 13, mp['A'] = 1;
    map<int, char> mp2;
    mp2[11] = 'J', mp2[12] = 'Q', mp2[13] = 'K', mp2[1] = 'A';
    while (getline(cin, s)) {
        if (s.find("joker") != s.npos || s.find("JOKER") != s.npos) {//包含大小王无法进行计算,输出ERROR
            cout << "ERROR" << endl;
            continue;
        }
        stringstream ss(s);
        string tmp;
        vector<int> num;
        while (getline(ss, tmp, ' ')) {//用空格切割获得四个数字
            if (!mp[tmp[0]]){//如果不是JQKA 则直接转为数字
                num.push_back(stoi(tmp));
            }else{//否则用mp映射到数字
                num.push_back(mp[tmp[0]]);
            }
        }
        sort(num.begin(), num.end());
        bool flag = false;
        char op[3];
        do {
            if (get(num[0], num[1], num[2], num[3], op[0], op[1], op[2])) {//如果这个排列可以完成24点运算
                if (mp2[num[0]]){//输出第一个数字,如果是11 12 13 1转换为J Q K A后再输出
                    cout << mp2[num[0]];
                }else{
                    cout<<num[0];
                }
                for (int i = 1; i < 4; i++) {
                    cout << op[i - 1];//输出运算符
                    if (mp2[num[i]]){
                        cout << mp2[num[i]];
                    }else{
                        cout << num[i];
                    }
                }
                cout << "\n";
                flag = true;//找了一种解法,flag为true
                break;
            }
        } while (next_permutation(num.begin(), num.end()));//每个排列都遍历一遍
        if (!flag){
            //没有找到解法
            cout << "NONE" << endl;
        }
    }


}


复杂度分析:

  • 时间复杂度:O(1)O(1),虽然过程繁琐,但是说到底是对4个数的运算进行枚举,只需要常数时间。
  • 空间复杂度:O(1)O(1),因为是对4个数进行运算,递归栈的大小是常数。

方法二:枚举

方法一中我们枚举了所有操作数可能的顺序,同样的,我们也可以对运算符进行枚举。最终我们只需在三个地方用到运算符,每个地方运算符都可能是+/+-*/中的一种,总共有4x4x4种可能,我们枚举所有操作数的顺序和运算符的64种可能,计算当前操作数顺序和运算符顺序下的结果,如果结果为24,则结束枚举,输出运算过程。运算符的64种顺序枚举用三个for循环实现,calculator函数计算当前操作数顺序和运算符的结果。

具体做法:

#include <bits/stdc++.h>
using namespace std;
map<char, int> mp;
vector<char> op={'+', '-', '*', '/'};
vector<char> operation;//当前的运算符顺序


int calculator(vector<int> num, vector<char> operation) {//计算当前顺序的操作数和运算符的结果
    int sum=num[0];
    for(int i=0,j=1;i<operation.size();i++,j++){
        switch(operation[i]){
            case '+':
                sum += num[j];
                break;
            case '-':
                sum -= num[j];
                break;
            case '*':
                sum *= num[j];
                break;
            case '/':
                sum /= num[j];
                break;
        }
    }
    return sum;
}

bool dfs(int a, int b, int c, int d) {
    vector<int> num = {a, b, c, d};
    for(int i=0;i<op.size();i++){//三个循环枚举所有的操作符顺序可能
        for(int j=0;j<op.size();j++){
            for(int k=0;k<op.size();k++){
                operation = {op[i], op[j], op[k]};//枚举所有可能的运算符顺序
                int sum = calculator(num, operation);//计算结果
                if(sum == 24){//若结果为24则结束枚举
                    return true;
                }
            }
        }
    }
    return false;
}

int main() {
    string s;
    mp['J'] = 11, mp['Q'] = 12, mp['K'] = 13, mp['A'] = 1;
    map<int, char> mp2;
    mp2[11] = 'J', mp2[12] = 'Q', mp2[13] = 'K', mp2[1] = 'A';
    while (getline(cin, s)) {
        if (s.find("joker") != s.npos || s.find("JOKER") != s.npos) {//包含大小王无法进行计算,输出ERROR
            cout << "ERROR" << endl;
            continue;
        }
        stringstream ss(s);
        string tmp;
        vector<int> num;
        while (getline(ss, tmp, ' ')) {//用空格切割获得四个数字
            if (!mp[tmp[0]]){//如果不是JQKA 则直接转为数字
                num.push_back(stoi(tmp));
            }else{//否则用mp映射到数字
                num.push_back(mp[tmp[0]]);
            }
        }
        sort(num.begin(), num.end());
        bool flag = false;
        char op[3];
        do {
            if (dfs(num[0], num[1], num[2], num[3])) {//如果这个操作数排列可以完成24点运算
                if (mp2[num[0]]){//输出第一个数字,如果是11 12 13 1转换为J Q K A后再输出
                    cout << mp2[num[0]];
                }else{
                    cout<<num[0];
                }
                for (int i = 1; i < 4; i++) {
                    cout << operation[i - 1];//输出运算符
                    if (mp2[num[i]]){
                        cout << mp2[num[i]];
                    }else{
                        cout << num[i];
                    }
                }
                cout << "\n";
                flag = true;//找了一种解法,flag为true
                break;
            }
        } while (next_permutation(num.begin(), num.end()));//每个排列都遍历一遍
        if (!flag){
            //没有找到解法
            cout << "NONE" << endl;
        }
    }


}


复杂度分析:

  • 时间复杂度:O(1)O(1),虽然有三个for循环,但是总共只有64种可能,只需要常数时间。
  • 空间复杂度:O(1)O(1),只用了常数空间。