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# (.NET Core 3.1.201) (judge verdict: Accepted)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class TEST{
	static void Main(){
		Sol mySol =new Sol();
		mySol.Solve();
	}
}

class Sol{
	bool IsPalindromic(String s){
		int n = s.Length;
		int l = n / 2, r = n / 2;
		if(n % 2 == 0) l--;
		while(l >= 0){
			if(s[l] != s[r]) return false;
			l--; r++;
		}
		return true;
	}
	bool IsPalindromic(String s, String t){
		return IsPalindromic(s + t);
	}
	const int M = 64;
	const int M2 = M * M;
	public void Solve(){
		
		Func<int, int, int, int> enc = (d, i, j) => d * M2 + i * M + j;
		
		long Inf = (long) 1e18;
		long ans = Inf;
		for(int i=0;i<N;i++){
			if(IsPalindromic(S[i])) ans = Math.Min(ans, C[i]);
		}
		
		long[][][] min = new long[2][][];
		for(int d=0;d<2;d++){
			min[d] = new long[N][];
			for(int i=0;i<N;i++){
				min[d][i] = new long[M];
				for(int j=0;j<M;j++){
					min[d][i][j] = Inf;
				}
			}
		}
		
		
		var SH = new SkewHeap<Pair>();
		for(int i=0;i<N;i++){
			for(int j=0;j<N;j++){
				String sl = S[i], sr = S[j];
				long c = C[i] + C[j];
				if(IsPalindromic(sl, sr)){
					ans = Math.Min(ans, c);
					continue;
				}
				
				int l = 0;
				int r = sr.Length - 1;
				
				while(true){
					if(l == sl.Length || r == -1) break;
					if(sl[l] != sr[r]) break;
					l++; r--;
				}
				
				if(l != sl.Length && r != -1) continue;
				if(l == sl.Length){
					int rest = r + 1;
					if(rest == 0){
						ans = Math.Min(ans, c);
					} else {
						min[1][j][rest] = c;
						SH.Push(new Pair(enc(1, j, rest), c));
					}
				}
				if(r == -1){
					int rest = sl.Length - l;
					if(rest == 0){
						ans = Math.Min(ans, c);
					} else {
						min[0][i][rest] = c;
						SH.Push(new Pair(enc(0, i, rest), c));
					}
				}
			}
		}
		
		bool[][][] used = new bool[2][][];
		for(int d=0;d<2;d++){
			used[d] = new bool[N][];
			for(int i=0;i<N;i++) used[d][i] = new bool[M];
		}
		while(SH.Count > 0){
			var p = SH.Top; SH.Pop();
			int dir = p.Pos / M2;
			int i0 = p.Pos / M % M;
			int len = p.Pos % M;
			if(used[dir][i0][len]) continue;
			used[dir][i0][len] = true;
			
			for(int k=0;k<N;k++){
				String sl, sr;
				int i, j;
				if(dir == 0){
					sl = S[i0].Substring(S[i0].Length - len);
					sr = S[k];
					i = i0; j = k;
				} else {
					sl = S[k];
					sr = S[i0].Substring(0, len);
					i = k; j = i0;
				}
				long c = p.Cost + C[k];
				
				if(IsPalindromic(sl, sr)){
					ans = Math.Min(ans, c);
					continue;
				}
				
				int l = 0;
				int r = sr.Length - 1;
				
				while(true){
					if(l == sl.Length || r == -1) break;
					if(sl[l] != sr[r]) break;
					l++; r--;
				}
				
				if(l != sl.Length && r != -1) continue;
				if(l == sl.Length){
					int rest = r + 1;
					if(rest == 0){
						ans = Math.Min(ans, c);
					} else {
						min[1][j][rest] = c;
						SH.Push(new Pair(enc(1, j, rest), c));
					}
				}
				if(r == -1){
					int rest = sl.Length - l;
					if(rest == 0){
						ans = Math.Min(ans, c);
					} else {
						min[0][i][rest] = c;
						SH.Push(new Pair(enc(0, i, rest), c));
					}
				}
				
			}
		}
		
		Console.WriteLine(ans != Inf ? ans : -1);
		
		
		
		
		
	}
	int N;
	String[] S;
	long[] C;
	public Sol(){
		N = ri();
		S = new String[N];
		C = new long[N];
		for(int i=0;i<N;i++){
			var ss = rsa();
			S[i] = ss[0];
			C[i] = long.Parse(ss[1]);
		}
	}

	static String rs(){return Console.ReadLine();}
	static int ri(){return int.Parse(Console.ReadLine());}
	static long rl(){return long.Parse(Console.ReadLine());}
	static double rd(){return double.Parse(Console.ReadLine());}
	static String[] rsa(char sep=' '){return Console.ReadLine().Split(sep);}
	static int[] ria(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>int.Parse(e));}
	static long[] rla(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>long.Parse(e));}
	static double[] rda(char sep=' '){return Array.ConvertAll(Console.ReadLine().Split(sep),e=>double.Parse(e));}
}


class Pair : IComparable<Pair> {
	public int Pos;
	public long Cost;
	public Pair(int p, long c) {
		Pos = p; Cost = c;
	}
	public int CompareTo(Pair t) {
		//return this.Cost > t.Cost ? -1 : this.Cost < t.Cost ? 1 : 0;
		return this.Cost > t.Cost ? 1 : this.Cost < t.Cost ? -1 : 0;
	}
}


class SkewHeap<T> where T:IComparable<T>{
	public int Count{
		get{return cnt;}
		private set{cnt=value;}
	}
	
	public SkewHeap(){
		root=null;
		this.Count=0;
	}
	
	public void Push(T v){
		NodeSH<T> p=new NodeSH<T>(v);
		root=NodeSH<T>.Meld(root,p);
		this.Count++;
	}
	
	public void Pop(){
		if(root==null)return;
		root=NodeSH<T>.Meld(root.L,root.R);
		this.Count--;
	}
	
	public T Top{
		get{return root.Val;}
	}
	
	int cnt;
	NodeSH<T> root;
	
	class NodeSH<S> where S : IComparable<S> {
		public NodeSH<S> L, R;
		public S Val;

		public NodeSH(S v){
			Val = v;
			L = null; R = null;
		}
		public static NodeSH<S> Meld(NodeSH<S> a, NodeSH<S> b){
			if(a == null)return b;
			if (b == null) return a;
			if (a.Val.CompareTo(b.Val) > 0) swap(ref a, ref b);
			a.R = Meld(a.R, b);
			swap(ref a.L, ref a.R);
			return a;
		}

		static void swap<U>(ref U x, ref U y) {
			U t = x; x = y; y = t;
		}
	}
}