Problem:
Score : 600 points

Problem Statement

We have N strings of lowercase English letters: S_1, S_2, \cdots, S_N.

Takahashi wants to make a string that is a palindrome by choosing one or more of these strings - the same string can be chosen more than once - and concatenating them in some order of his choice.

The cost of using the string S_i once is C_i, and the cost of using it multiple times is C_i multiplied by that number of times.

Find the minimum total cost needed to choose strings so that Takahashi can make a palindrome.

If there is no choice of strings in which he can make a palindrome, print -1.

Constraints

1 \leq N \leq 50

1 \leq |S_i| \leq 20

S_i consists of lowercase English letters.

1 \leq C_i \leq 10^9

Input

Input is given from Standard Input in the following format:

N
S_1 C_1
S_2 C_2
:
S_N C_N

Output

Print the minimum total cost needed to choose strings so that Takahashi can make a palindrome, or -1 if there is no such choice.

Sample Input 1

3
ba 3
abc 4
cbaa 5

Sample Output 1

7

We have ba, abc, and cbaa.

For example, we can use ba once and abc once for a cost of 7, then concatenate them in the order abc, ba to make a palindrome.
Also, we can use abc once and cbaa once for a cost of 9, then concatenate them in the order cbaa, abc to make a palindrome.

We cannot make a palindrome for a cost less than 7, so we should print 7.

Sample Input 2

2
abcab 5
cba 3

Sample Output 2

11

We can choose abcab once and cba twice, then concatenate them in the order abcab, cba, cba to make a palindrome for a cost of 11.

Sample Input 3

4
ab 5
cba 3
a 12
ab 10

Sample Output 3

8

We can choose a once, which is already a palindrome, but it is cheaper to concatenate ab and cba.

Sample Input 4

2
abc 1
ab 2

Sample Output 4

-1

We cannot make a palindrome, so we should print -1.

Solution in C++ (Clang 10.0.0) (judge verdict: Accepted)

#include <cassert>
#include <queue>
#include <vector>
#include <iostream>
using namespace std;

bool rev(string &s, int i, int j, string &t, int k, int l) {
    assert(j - i == l - k);
    assert(j <= s.size());
    assert(l <= t.size());
    while (i < j) {
        if (s[i] != t[l-1]) return false;
        i++; l--;
    }
    return true;
}

bool palindrome(string &s, int i, int j) {
    assert (i <= j);
    assert (j <= s.size());
    return rev(s, i, j, s, i, j);
}

int main() {
    int N; cin >> N;
    string s[N];
    int C[N];
    for (int i = 0; i < N; i++) cin >> s[i] >> C[i];
    int pref[N][21], suf[N][21], idx = 0;
    const int goal = idx++;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j <= s[i].size(); j++) {
            pref[i][j] = idx++;
            suf[i][j] = idx++;
        }
    }
    vector<pair<int, int> > adj[idx];
    for (int i = 0; i < N; i++) {
        for (int j = 0; j <= s[i].size(); j++) {
            for (int k = 0; k < N; k++) {
                const int ni = s[i].size(), nk = s[k].size();
                if (j > nk) {
                    if (rev(s[i], j-nk, j, s[k], 0, nk)) {
                        adj[pref[i][j]].push_back({pref[i][j-nk], C[k]});
                    }
                    if (rev(s[i], ni-j, ni-j+nk, s[k], 0, nk)) {
                        adj[suf[i][j]].push_back({suf[i][j-nk], C[k]});
                    }
                } else {
                    if (rev(s[i], 0, j, s[k], 0, j)) {
                        adj[pref[i][j]].push_back({suf[k][nk-j], C[k]});
                    }
                    if (rev(s[i], ni-j, ni, s[k], nk-j, nk)) {
                        adj[suf[i][j]].push_back({pref[k][nk-j], C[k]});
                    }
                }
            }
            if (palindrome(s[i], 0, j))
                adj[pref[i][j]].push_back({goal, 0});
            if (palindrome(s[i], s[i].size()-j, s[i].size()))
                adj[suf[i][j]].push_back({goal, 0});
        }
    }

    long long dist[idx];
    const long long INF = 1LL<<60;
    fill(dist, dist+idx, INF);
    priority_queue<pair<long long, int> > pq;
    for (int i = 0; i < N; i++) {
        int u = pref[i][s[i].size()], v = suf[i][s[i].size()];
        dist[u] = dist[v] = C[i];
        pq.push({-C[i], u});
        pq.push({-C[i], v});
    }
    while (!pq.empty()) {
        auto [d, u] = pq.top();
        d = -d;
        pq.pop();
        if (d > dist[u]) continue;
        for (auto [v, e] : adj[u]) {
            if (d + e < dist[v]) {
                dist[v] = d + e;
                pq.push({-d-e, v});
            }
        }
    }
    // for (int i = 0; i < idx; i++) cerr << i << " " << dist[i] << endl;
    // for (int i = 0; i < idx; i++) {
    //     for (auto [u, d] : adj[i])
    //         cerr << i << " " << u << " " << d << endl;
    // }
    cout << (dist[goal] == INF ? -1 : dist[goal]) << endl;
}