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++ (GCC 9.2.1) (judge verdict: Wrong Answer)

#line 1 "/workspaces/compro/lib/graph/edge.hpp"


template <typename T> struct edge {
  int to;
  T cost;
};

#line 2 "/workspaces/compro/lib/graph/dijkstra.hpp"
#include <climits>
#include <limits>
#include <queue>
#include <vector>

#ifndef DIJKSTRA
#define DIJKSTRA

// sからの各頂点への最短距離を返す。計算量はO(E logV)
template <class T> std::vector<T> dijkstra(const int s, const std::vector<std::vector<edge<T>>> &g) {
  std::priority_queue<std::pair<T, int>, std::vector<std::pair<T, int>>, std::greater<std::pair<T, int>>> q;

  constexpr T MAX = std::numeric_limits<T>::max();
  std::vector<T> d(g.size(), MAX);
  d[s] = 0;
  q.push(std::make_pair(d[s], s));

  while (!q.empty()) {
    const auto p = q.top();
    q.pop();

    const int v = p.second;
    if (d[v] < p.first)
      continue;

    for (const auto e : g[v]) {
      const int u = e.to;
      const T next_cost = d[v] + e.cost;
      if (d[u] <= next_cost)
        continue;
      d[u] = next_cost;
      q.push(std::make_pair(d[u], u));
    }
  }
  return d;
}

#endif
#line 1 "/workspaces/compro/lib/template.hpp"


#line 1 "/workspaces/compro/lib/io/vector.hpp"
#include <iostream>
#line 3 "/workspaces/compro/lib/io/vector.hpp"

#ifndef IO_VECTOR
#define IO_VECTOR

template <class T> std::ostream &operator<<(std::ostream &out, const std::vector<T> &v) {
  int size = v.size();
  for (int i = 0; i < size; i++) {
    std::cout << v[i];
    if (i != size - 1)
      std::cout << " ";
  }
  return out;
}

template <class T> std::istream &operator>>(std::istream &in, std::vector<T> &v) {
  for (auto &el : v) {
    std::cin >> el;
  }
  return in;
}

#endif
#line 4 "/workspaces/compro/lib/template.hpp"
#include <bits/stdc++.h>
#define REP(i, n) for (int i = 0; i < n; i++)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define ALL(v) (v).begin(), (v).end()
#define coutd(n) cout << fixed << setprecision(n)
#define ll long long int
#define vl vector<ll>
#define vi vector<int>
#define MM << " " <<

using namespace std;

template <class T> void say(bool val, T yes, T no) { cout << (val ? yes : no) << "\n"; }

void say(bool val, string yes = "Yes", string no = "No") { say<string>(val, yes, no); }

template <class T> void chmin(T &a, T b) {
  if (a > b)
    a = b;
}

template <class T> void chmax(T &a, T b) {
  if (a < b)
    a = b;
}

// C++ 17に完全移行したら消す
// 最大公約数を求める
template <class T> T gcd(T n, T m) { return n ? gcd(m % n, n) : m; }

// 最小公倍数を求める
template <class T> T lcm(T n, T m) {
  int g = gcd(n, m);
  return n * m / g;
}

// 重複を消す。計算量はO(NlogN)
template <class T> void unique(std::vector<T> &v) {
  std::sort(v.begin(), v.end());
  v.erase(std::unique(v.begin(), v.end()), v.end());
}


#line 3 "main.cpp"

bool isPa(string &s) {
  int i = 0, j = s.length() - 1;
  while (i < j) {
    if (s[i] != s[j])
      return false;
  }
  return true;
}

long long solve(int N, const std::vector<std::string> &S, const std::vector<long long> &C) {
  // prefixとsuffixからなる頂点を作成、どっちに属しているかを識別するためにmap<pair<string,int>,int>でindexを持つ
  map<pair<string, int>, int> mp; // prefixならsecondが0、suffixなら1

  vector<string> revS(N);
  REP(i, N) { revS[i] = string(S[i].rbegin(), S[i].rend()); }

  int idx_cnt = 0;
  auto add_substr = [&idx_cnt](string str, map<pair<string, int>, int> &mp, bool is_suffix) -> void {
    int len = str.length();
    REP(i, len + 1) {
      pair<string, int> target = {str.substr(0, i), is_suffix};
      if (mp.find(target) != mp.end())
        continue;
      mp[{str.substr(0, i), is_suffix}] = idx_cnt;
      idx_cnt++;
    }
  };

  REP(i, N) {
    add_substr(S[i], mp, false);
    add_substr(revS[i], mp, true);
  }

  int size = mp.size();
  vector<vector<edge<ll>>> g(size + 1);

  // 辺を張る。その文字列を適用したらどうなるのかを考えながら頑張る
  for (const auto &[p, idx] : mp) {
    const auto [str, isSuffix] = p;
    if (str == "")
      continue;
    REP(i, N) {
      int len = str.length();
      if (isSuffix) {
        // prefixの中から対応するものを探す
        int mini = min(str.length(), S[i].length());
        int maxi = max(str.length(), S[i].length());

        if (str.substr(0, mini) != S[i].substr(0, mini))
          continue;

        if (str.length() > S[i].length()) {
          g[idx].push_back({mp[{str.substr(mini, maxi - mini), 1}], C[i]});
        } else {
          g[idx].push_back({mp[{S[i].substr(mini, maxi - mini), 0}], C[i]});
        }
      } else {
        // suffixの中から対応するものを探す
        int mini = min(str.length(), revS[i].length());
        int maxi = max(str.length(), revS[i].length());

        if (str.substr(0, mini) != revS[i].substr(0, mini))
          continue;

        if (str.length() > revS[i].length()) {
          g[idx].push_back({mp[{str.substr(mini, maxi - mini), 0}], C[i]});
        } else {
          g[idx].push_back({mp[{revS[i].substr(mini, maxi - mini), 1}], C[i]});
        }
      }
    }
  }

  // ゴール状態を追加し、空文字列や回文の状態からゴール状態に遷移する辺を追加する。
  for (const auto &[p, idx] : mp) {
    const auto &[str, isSuffix] = p;
    string rev = string(str.rbegin(), str.rend());
    if (rev == str) {
      g[idx].push_back({size, 0});
    }
  }

  // 各文字列を先頭に使ったとしてdijkstraをN回チェック。
  ll ans = 1e18;
  REP(i, N) {
    auto d = dijkstra(mp[{S[i], 0}], g);
    if (d[size] != numeric_limits<ll>::max())
      chmin(ans, d[size] + C[i]);
  }

  // ゴール頂点までの距離が答え。
  return ans == 1e18 ? -1 : ans;
}

// generated by online-judge-template-generator v4.4.0 (https://github.com/kmyk/online-judge-template-generator)
int main() {
  int N;
  std::cin >> N;
  std::vector<string> S(N);
  std::vector<long long> C(N);
  for (int i = 0; i < N; ++i) {
    std::cin >> S[i] >> C[i];
  }
  auto ans = solve(N, S, C);
  std::cout << ans << std::endl;
  return 0;
}