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 Java (OpenJDK 11.0.6) (judge verdict: Accepted)

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {
    
    private static int N;
    private static String[] Ss;
    private static int[] Cs;
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        N = sc.nextInt();
        Ss = new String[N];
        Cs = new int[N];
        
        for (int i = 0; i < N; i++) {
            Ss[i] = sc.next();
            Cs[i] = sc.nextInt();
        }
        
        long min = Long.MAX_VALUE;
        
        // 単体で回文になっているものがあるか調べる
        for (int i = 0; i < N; i++) {
            if (isPalindrome(Ss[i]) && Cs[i] < min) {
                min = Cs[i];
            }
        }
        
        // 左端と右端の文字列を選び、その場合の最小コストを調べる
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                long cost = cost(Ss[i], Ss[j]);
                if (cost < min - Cs[i] - Cs[j]) {
                    min = cost + Cs[i] + Cs[j];
                }
            }
        }
        
        System.out.println(min == Long.MAX_VALUE ? -1 : min);
    }
    
    private static boolean isPalindrome(String str) {
        int i = 0;
        int j = str.length() - 1;
        while (i < j) {
            if (str.charAt(i) != str.charAt(j)) {
                return false;
            }
            i++;
            j--;
        }
        return true;
    }
    
    private static boolean isSameEdge(String left, String right) {
        int i = 0;
        int j = right.length() - 1;
        while (i < left.length() && 0 <= j) {
            if (left.charAt(i) != right.charAt(j)) {
                return false;
            }
            i++;
            j--;
        }
        return true;
    }
    
    private static final Map<String, Long> costs = new HashMap<>();
    
    private static long cost(String left, String right) {
        String key = left + "_" + right;
        if (costs.containsKey(key)) {
            return costs.get(key);
        } else {
            costs.put(key, Long.MAX_VALUE);
        }
        
        if (!isSameEdge(left, right)) {
            return Long.MAX_VALUE;
        }
        if (left.length() == right.length()) {
            costs.put(key, 0L);
            return 0;
        }
        
        String rem = left.length() < right.length()
                ? right.substring(0, right.length() - left.length())
                : left.substring(right.length());
        if (isPalindrome(rem)) {
            costs.put(key, 0L);
            return 0;
        }
        
        long min = Long.MAX_VALUE;
        
        if (left.length() < right.length()) {
            for (int i = 0; i < N; i++) {
                long cost = cost(Ss[i], rem);
                if (cost < min - Cs[i]) {
                    min = cost + Cs[i];
                }
            }
            
        } else {
            for (int i = 0; i < N; i++) {
                long cost = cost(rem, Ss[i]);
                if (cost < min - Cs[i]) {
                    min = cost + Cs[i];
                }
            }
        }
        costs.put(key, min);
        return min;
    }
}