予鈴

アウトプットとメモ書きの中間みたいな記事がたくさん出ます。

整数4つを10にするやつを解く

題名のまんまです。
1,3,3,7 から (1 + 7 / 3) * 3 みたいなパターンを導き出して計算します。

解法

再帰をぶん回す。全探索です。
括弧さえつかなければ、適当に演算子を全探索してevalすればいいが、問題は括弧の場所がめんどくさい。
全探索する手法も考えられるが、括弧を付ける位置は計算順序を変化させるだけなので、計算する2つの項を探索、2つの数字の組を計算し式を圧縮して再帰すれば良い

免責

・可読性が最悪
・多分無駄な計算が多い(dpできるらしい)
 10^{-6}ぐらいは誤差が出ます。(分母と分子のペアを持つことで誤差消えるらしい)
・計算過程は出ますが、最初から括弧がついた形で出力はされません。

#include <vector>
#include <string>
#include <iostream>
#include <numeric>
#include <algorithm>
#include <functional>
#include <stack>
#include <map>
using namespace std;
 
bool isOp(string c){return c == "+" or c == "*" or c == "&" or c == "/" or c == ")" or c == "("; }
bool isOp(char c){return c == '+' or c == '*' or c == '&' or c == '/';}
bool isNum(char c){return ('0' <= c and c <= '9') || c == '-';}
bool isNum(string s){return !(isOp(s));}
constexpr double eps = 1e-6;

double interpreter(string lhs, string op, string rhs){
    double lh = stod(lhs); double rh = stod(rhs);
    if(op == "+") return lh + rh;
    if(op == "&") return lh - rh;
    if(op == "*") return lh * rh;
    if(rh == 0){
        return -99999999;
    }
    return lh / rh;
}
map<string,int>memo;
string ans = "";
void rec2(string exp,string& original, vector<string>path, int len = 3){
 
    vector<string>parser;
 
    for(auto c : exp){
        if(parser.empty()){
            parser.push_back(string(1,c));
        } else if(isOp(c)){
            parser.push_back(string(1,c));
        } else if(isOp(parser.back()) && isNum(c)){
            parser.push_back(string(1,c));
        } else {
            parser.back() += string(1,c);
        }
    }
    path.push_back(exp);
 
    if(len == 0){
        if(abs(10 - stod(parser.front())) <= eps && memo[original] == 0){
            memo[original] = 1;
            std::cout << "find !!" << endl;
            auto f = [](string s){
                string t;
                for(auto& c : s)
                    t.push_back(c ==  '&' ? '-' : c);
                return t;
            };
            cout << f(original) << endl;
            for(auto p : path){
                cout <<  f(p) << endl;
            }
        }
        return;
    }
    if(len >= 1){
        double value = interpreter(parser[0],parser[1],parser[2]);
        auto cp = to_string(value);
        if(len >= 2 ){
            cp += accumulate(parser.begin() + 3, parser.end(),""s,[](auto lhs, auto rhs){
                return lhs + rhs;
            });
        }
        rec2(cp,original,path,len - 1);
    }
    if(len >= 2){
        double value = interpreter(parser[2],parser[3],parser[4]);
        auto cp = parser[0] + parser[1] + to_string(value);
        if(len >= 3){
            cp += accumulate(parser.begin() + 5, parser.end(),""s,[](auto lhs, auto rhs){
            return lhs + rhs;
        });
        }
        rec2(cp,original,path,len - 1);
    }
    if(len >= 3){
        double value = interpreter(parser[4],parser[5], parser[6]);
        auto cp = accumulate(parser.begin(), parser.begin() + 4,""s,[](auto lhs, auto rhs){
            return lhs + rhs;
        }) + to_string(value);
        rec2(cp,original,path,len - 1);
    }
}
void  rec(string& op, string exp, vector<double>& in,int depth = 0){
    if(depth + 1 == in.size()){
        vector<string>p;
        rec2(exp,exp,p);
        return;
    } else {
        for(int i = 0; i < op.size(); ++i){
            string cp = exp;
            cp += op[i] + to_string(in[depth + 1]);
            rec(op,cp,in,depth + 1);
        }
    }
}
 
int main() {
    string in;
    string op = "+&/*";
 
    cin >> in;
    auto num = [&](){
        vector<double>res;
        string t;
        for(auto c : in){
            if(c == ','){
                res.push_back(stoi(t));
                t.clear();
 
            } else {
                t.push_back(c);
            }
        }
        res.push_back(stoi(t));
        return res;
    }();
    sort(num.begin(),num.end());
    do{
        rec(op,to_string(num[0]),num);
    } while(next_permutation(num.begin(),num.end()));

    return 0;
}