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: Wrong Answer)

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class Main {
	int num = 0;
	//入力値
	int      N;
	String[] S;
	long[]   C;
	
	class Element implements Comparable<Element>{
		int N;
		String left, right;
		long cost = 0L;
		
		public Element() {
			this.N = num;
			num++;
		}
		
		public Element(int index){
			this();
			left  = "";
			right = S[index];
			cost  = C[index];
		}
		
		public Element(int left, int right) {
			this();
			this.left  = S[left];
			this.right = S[right];
			this.cost  = C[left] + C[right];
		}
		
		public Element(String left, String right, long cost) {
			this();
			this.left  = left;
			this.right = right;
			this.cost  = cost;
		}
		
		@Override
		public int compareTo(Element o) {
			if (cost > o.cost)
				return  1;
			else if (cost < o.cost)
				return -1;
			else
				return  this.N - o.N;
		}
	}
	
	public Main() {
		FastScanner scanner = new FastScanner();
		N = scanner.nextInt();
		S = new String[N];
		C = new long[N];
		
		TreeSet<Element> palindromeSet = new TreeSet<>();
		for (int i = 0; i < N; i++) {
			S[i] = scanner.next();
			C[i] = scanner.nextLong();
			
			palindromeSet.add( new Element(i) );
		}
		
		for (int l = 0; l < N; l++) {
			for (int r = 0; r < N; r++) {
				if (l == r) continue;
				palindromeSet.add( new Element(l,r) );
			}
		}
		
		while( !palindromeSet.isEmpty() ){
			Element elem = palindromeSet.pollFirst();
			
			if ( isPalindrome(elem.left + elem.right) ) {
				System.out.println(elem.cost);
				return;
			}
			
			StringBuilder L = new StringBuilder(elem.left);
			StringBuilder R = new StringBuilder(elem.right);
			boolean isRightLonger = elem.left.length() < elem.right.length();
			
			boolean isContinue = false;
			while(L.length() != 0 && R.length() != 0){
				// 回文にならない
				if (L.charAt(0) != R.charAt(R.length()-1)){
					isContinue = true;
					break;
				}
				
				L.deleteCharAt(0);
				R.deleteCharAt(R.length()-1);
			}
			
			if (isContinue) continue;
			
			if ( isRightLonger ) {
				//すでに回文である
				if (isPalindrome(R.toString())) {
					System.out.println(elem.cost);
					return;
				}
				
				String reverse = R.reverse().toString();
				for (int i = 0; i < N; i++) {
					if ( S[i].startsWith(reverse) ) {
						palindromeSet.add( new Element(S[i], R.toString(), elem.cost + C[i]) );
					}
				}
				
			} else {
				//終了条件：すでに回文である
				if (isPalindrome(L.toString())) {
					System.out.println(elem.cost);
					return;
				}
				
				String reverse = L.reverse().toString();
				for (int i = 0; i < N; i++) {
					if ( S[i].endsWith(reverse) ) {
						palindromeSet.add( new Element(L.toString(), S[i], elem.cost + C[i]) );
					}
				}
			}
			
		}
		
		System.out.println(-1);
	}
	
	// 文字列sが回文かどうかを判定する．
	// 0文字はtrue
	boolean isPalindrome(String s){
		int len = s.length();
		
		for (int i = 0; 2*i + 1 < len; i++) {
			if (s.charAt(i) != s.charAt(len - i - 1))
				return false;
		}
		
		return true;
	}
	
	public static void main(String[] args) {
		new Main();
	}
}

class FastScanner {
	private final InputStream in = System.in;
	private final byte[] buffer = new byte[1024];
	private int ptr = 0;
	private int buflen = 0;
	
	private boolean hasNextByte() {
		if (ptr < buflen) {
			return true;
		} else {
			ptr = 0;
			try {
				buflen = in.read(buffer);
			} catch (IOException e) {
				e.printStackTrace();
			}
			if (buflen <= 0) {
				return false;
			}
		}
		return true;
	}
	
	private int readByte() {
		if (hasNextByte()) return buffer[ptr++];
		else return -1;
	}
	
	private static boolean isPrintableChar(int c) {
		return 33 <= c && c <= 126;
	}
	
	public boolean hasNext() {
		while (hasNextByte() && !isPrintableChar(buffer[ptr])) ptr++;
		return hasNextByte();
	}
	
	public String next() {
		if (!hasNext()) throw new NoSuchElementException();
		StringBuilder sb = new StringBuilder();
		int b = readByte();
		while (isPrintableChar(b)) {
			sb.appendCodePoint(b);
			b = readByte();
		}
		return sb.toString();
	}
	
	public long nextLong() {
		if (!hasNext()) throw new NoSuchElementException();
		long n = 0;
		boolean minus = false;
		int b = readByte();
		if (b == '-') {
			minus = true;
			b = readByte();
		}
		if (b < '0' || '9' < b) {
			throw new NumberFormatException();
		}
		while (true) {
			if ('0' <= b && b <= '9') {
				n *= 10;
				n += b - '0';
			} else if (b == -1 || !isPrintableChar(b)) {
				return minus ? -n : n;
			} else {
				throw new NumberFormatException();
			}
			b = readByte();
		}
	}
	
	public int nextInt() {
		long nl = nextLong();
		if (nl < Integer.MIN_VALUE || nl > Integer.MAX_VALUE) throw new NumberFormatException();
		return (int) nl;
	}
	
	public double nextDouble() {
		return Double.parseDouble(next());
	}
}