Необходимо найти множество из пяти простых чисел с минимальной суммой такое, что после “склеивания” в любом порядке любых двух чисел из него тоже будет простое число. Здесь под процедурой “склеивания” чисел \(a\) и \(b\) подразумевается получения из \(a = \overline{a_1 a_2 \ldots a_n}\) и \(b = \overline{b_1 b_2 \ldots b_m}\) некоторого \(c\) так, что \(c = \overline{a_1 a_2 \ldots a_n b_1 b_2 \ldots b_m}\).

Полное условие можно найти тут

Для начала, можно понять, что непосредственный перебор “в лоб” слишком медленный и нужного результата не даст. Поэтому хочется уйти от, как мне кажется, не самого формализуемого условия к чему-то более простого, с чем проще работать. Давайте сначала поймём, какие вообще числа могут быть в одном множестве. Для этого достаточно перебрать все разбиения на два подчисла всех простых чисел. Это достаточно быстро, порядка \(O\left( N \right)\) операций. Важно не забыть, что мы можем разбивать число \(p\) только на \(\overline{p_1 p_2}\), между числами не может быть нулей! То есть если число 37 разбивается на 3 и 7, то 307 нет.

Пусть мы получили набор таких разбиений, то есть набор пар вида \(\left( p, q \right)\). Давайте составим из них граф, где вершинки это простые числа, а ориентированное ребро из \(p\) в \(q\) означает, что есть пара \(\left( p, q \right)\). Из того, что порядок склеивания чисел произвольный сразу следует, что рассматриваемый граф должен быть неориентированным. Таким образом, все пары \(\left( p, q \right)\) для которых нет пары \(\left( q, p \right)\) необходимо выкинуть, а из оставшихся построить граф.

Теперь задача стало гораздо понятнее: достаточно выбрать клику размера 5, что сумма значений её вершин минимальна. В общем случае, это достаточно ресурсоёмкая(как мне кажется) задача, но в реальном графе количество рёбер не слишком большое. В худшем случае, по теореме Турана, количество рёбер в графе лишь с одной такой кликой примерно на 10% меньше числа рёбер в полном графе.

Непосредственно сам поиск такой клики можно реализовать тривиально. Ниже мой код на C++11 с использованием библиотеки Boost Graph Library (BGL).

#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <cstdint>

#include <boost/graph/adjacency_list.hpp>
#include "primesieve.hpp"

using namespace std;
using namespace boost;

typedef adjacency_list<listS, vecS, undirectedS> Graph;

template <class T> 
vector< T > getDigs(T num) {
    vector< T > res;
    while ( num != 0 ) {
        res.push_back(num % 10);
        num = num / 10;
    }
    reverse(res.begin(),res.end());
    return res;
}

template <class T>
T getNum(const vector<T> &digs){
    T res(0);
    T cur_mult(1);
    for (auto it = digs.rbegin(); it != digs.rend(); ++it){
        res += *it * cur_mult;
        cur_mult *= T(10);
    }
    return res;
}

template <class T>
vector< pair<T,T> > getPrimePairs(T prime, set<T> &primes) {
    vector< pair<T,T> > res;
    auto digs = getDigs<T>(prime);
    for (size_t i = 1; i <= digs.size() - 1; ++i){
        T q1 = getNum(vector<T>(digs.begin(), digs.begin() + i));
        T q2 = getNum(vector<T>(digs.begin() + i, digs.end()));

        if ( (primes.find(q1) != primes.end())
          && (primes.find(q2) != primes.end()) && (digs.at(i) != 0) ){
            res.push_back(make_pair(q1,q2));
          }
    }
    return res;
}

// removes all (p,q) that (p,q) in edges but (q,p) is not
template <class T>
void delBadEdges(vector< pair<T,T> > &edges){
    map< int64_t, int > mults; // contains counts of p * q

    for (auto p: edges) {
        int64_t key = p.first * p.second;
        if ( mults.find(key) != mults.end() )
            mults[key] += 1;
        else
            mults[key] = 1;
    }
    vector< pair<T,T> > res;
    for (auto p: edges) {
        int64_t key = p.first * p.second;
        if ( mults[key] == 2 ){
            res.push_back(p);
            // edge (p,q) is already exists
            mults[key] = 0;
        }
    }
    swap(res, edges);
}

template <class T> 
set<T> getUniqNums(vector< pair<T,T> > &edges){
    set<T> res;
    for (auto p: edges) {
        res.insert(p.first);
        res.insert(p.second);        
    }
    return res;
}

template <class T>
void getNeedSet(Graph &g, Graph::vertex_descriptor st,int depth, 
    set<Graph::vertex_descriptor> cur_set, const map<Graph::vertex_descriptor, T> &vert_prime,
    vector< set<Graph::vertex_descriptor> > &v_out){
    auto edges = out_edges(st, g);
    vector< Graph::vertex_descriptor > adj_verts;
    for (auto e_it = edges.first; e_it != edges.second; ++e_it)
        adj_verts.push_back(target(*e_it,g));

    for (auto prev_v: cur_set)
        if ( find(adj_verts.begin(), adj_verts.end(), prev_v) == adj_verts.end() )
            return;

    cur_set.insert(st);

    if ( depth == 1 ){
        v_out.push_back(cur_set);
        return;
    }

    for (auto v: adj_verts){
        if ( (vert_prime.at(v) < vert_prime.at(st)) )
            continue;
        getNeedSet(g,v,depth - 1, cur_set, vert_prime, v_out);
    }
}

int main(int argc, char **argv) {
    const int MAX_VAL = 100000000;
    vector< long > v_primes;
    primesieve::generate_primes(MAX_VAL, &v_primes);
    set< long >    s_primes(v_primes.begin(), v_primes.end());

    cout << "Number of primes is " << v_primes.size() << endl;
    v_primes.clear();

    vector< pair< long,long > > all_edges;
    for (auto pr: s_primes){
        auto t_v = getPrimePairs(pr, s_primes);
        copy(t_v.begin(),t_v.end(),back_inserter(all_edges));
    }
    cout << "There are " << all_edges.size() << " edges" << endl;
    delBadEdges(all_edges);
    cout << "After deleting bad edges: " << all_edges.size() << " edges" << endl;
    // We only need this primes
    s_primes = getUniqNums(all_edges);
    map< Graph::vertex_descriptor, long > vert_prime;
    map< long, Graph::vertex_descriptor > prime_vert;
    Graph g;
    for (auto pr: s_primes){
        vert_prime[add_vertex(g)] = pr;
    }
    for (auto v: vert_prime){
        prime_vert[v.second] = v.first;
    }
    for (auto p: all_edges)
        add_edge(prime_vert.at(p.first),prime_vert.at(p.second),g);
    auto all_vertices = vertices(g);
    vector< set<Graph::vertex_descriptor> > goods;
    for (auto v_it = all_vertices.first; v_it != all_vertices.second; ++v_it){
        getNeedSet(g, *v_it,5, set<Graph::vertex_descriptor>(), vert_prime, goods);
    }
    vector< pair<size_t, size_t> > good_sums;
    for (size_t i = 0; i < goods.size(); ++i){
        cout << "Good set #" << i+1 << ": ";
        size_t cur_sum = 0;
        for (auto el: goods[i]){
            el = vert_prime.at(el);
            cur_sum += el;
            cout << el << " ";
        }
        cout << endl;
        good_sums.push_back(make_pair(cur_sum, i));
    }
    if ( !good_sums.empty() )
        cout << "Result is " << min_element(good_sums.begin(),good_sums.end(), [](pair<size_t, size_t> a, pair<size_t, size_t> b){return a.first < b.first;})->first << endl;
    return 0;
}

Ответ: 26033

UPD. Кажется, getNeedSet можно было реализовать гораздо проще, использую BGL посетителей (Visitors). Но, к сожалению, на этапе написания кода я про это забыл.