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 Rust (1.42.0) (judge verdict: Accepted)

#[allow(unused_imports)]
use {
   proconio::{fastout, input, marker::*},
   std::cmp::*,
   std::collections::*,
};

trait Monoid {
   fn id() -> Self;
   fn f(a: Self, b: Self) -> Self;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum MinInt {
   Val(i64),
   Maximal,
}
impl std::ops::MulAssign for MinInt {
   fn mul_assign(&mut self, other: Self) {
      if other < *self {
         *self = other
      };
   }
}
impl std::ops::Mul for MinInt {
   type Output = Self;
   fn mul(self, other: Self) -> Self {
      if self < other {
         self
      } else {
         other
      }
   }
}
impl Monoid for MinInt {
   fn id() -> Self { MinInt::Maximal }
   fn f(a: Self, b: Self) -> Self { a * b }
}
use MinInt::*;

fn dfs(
   lr: usize,
   cost: i64,
   zan: &Vec<u8>,
   ss: &Vec<Vec<u8>>,
   cs: &Vec<i64>,
   yet: &mut BTreeMap<Vec<u8>, i64>,
) -> MinInt {
   if let Some(x) = yet.get_mut(zan) {
      if *x <= cost {
         return Maximal;
      }
      *x = cost;
   } else {
      yet.insert(zan.to_vec(), cost);
   }
   let n = zan.len();
   {
      let mut fin = true;
      for i in 0..n / 2 {
         if zan[i] != zan[n - 1 - i] {
            fin = false;
            break;
         }
      }
      if fin {
         return Val(cost);
      }
   }
   let mut res = Maximal;
   if lr == 0 {
      for (s, &c) in ss.iter().zip(cs.iter()) {
         let m = s.len();
         let mut i = 0;
         while i < min(m, n) {
            if zan[i] != s[m - 1 - i] {
               break;
            }
            i += 1;
         }
         if i == min(m, n) {
            let mut z = vec![];
            if i == m {
               for i in m..n {
                  z.push(zan[i]);
               }
               res *= dfs(0, cost + c, &z, ss, cs, yet);
            } else {
               for i in 0..m - n {
                  z.push(s[i]);
               }
               res *= dfs(1, cost + c, &z, ss, cs, yet);
            }
         }
      }
   } else {
      for (s, &c) in ss.iter().zip(cs.iter()) {
         let m = s.len();
         let mut i = 0;
         while i < min(m, n) {
            if zan[n - 1 - i] != s[i] {
               break;
            }
            i += 1;
         }
         if i == min(m, n) {
            let mut z = vec![];
            if i == m {
               for i in 0..n - m {
                  z.push(zan[i]);
               }
               res *= dfs(1, cost + c, &z, ss, cs, yet);
            } else {
               for i in n..m {
                  z.push(s[i]);
               }
               res *= dfs(0, cost + c, &z, ss, cs, yet);
            }
         }
      }
   }
   res
}

#[fastout]
fn main() {
   input! {
      n: usize,
      sc: [(Bytes, i64); n]
   }

   let mut s = vec![];
   let mut c = vec![];
   for (a, b) in sc.iter().clone() {
      s.push(a.clone());
      c.push(*b);
   }

   let mut ans = Maximal;
   let mut yet = BTreeMap::new();
   for i in 0..n {
      ans *= dfs(0, c[i], &s[i], &s, &c, &mut yet);
   }
   if let Val(x) = ans {
      println!("{}", x);
   } else {
      println!("{}", -1);
   }
}