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: Time Limit Exceeded)

#![allow(
  non_snake_case,
  unused_variables,
  unused_assignments,
  unused_mut,
  unused_imports,
  unused_macros,
  dead_code
)]
//use proconio::fastout;
use proconio::{input, marker::*};
use std::cmp::*;
use std::collections::*;
//use std::collections::VecDeque;

macro_rules! debug {
  ($($a:expr),* $(,)*) => {
      #[cfg(debug_assertions)]
      eprintln!(concat!($("| ", stringify!($a), "={:?} "),*, "|"), $(&$a),*);
  };
}

fn main() {
  input! {
     N:usize,
     SC:[(Chars,i64);N]
  }
  let N = N;
  let mut SC: Vec<(Vec<char>, i64)> = SC;
  SC.sort_by_key(|(s, c)| *c);
  let mut RC: Vec<(Vec<char>, i64)> = SC
    .iter()
    .map(|(s, c)| {
      let mut r = s.clone();
      r.reverse();
      (r, *c)
    })
    .collect();
  debug!(SC);
  debug!(RC);
  // leftに追加
  // 回文成立チェック
  // (1) leftまたはrightの短いほうに一個ずつ追加可能かどうかをチェック。
  //   つまり、leftに追加したものの先頭とrightに追加したものについて、長さをS1,S2とすると、
  //   min(S1,S2)までが共通になるものをチェック。
  // 共通になるものがあれば、追加して回文成立チェック。
  // 不成立なら、(cost,left,right)の組をQueueに追加。(この時共通の部分列は削除)
  // 成立したなら、いったんcostを記憶して、(1)へ。
  // 一巡したなら、次のLeftに対して順次実行。
  // Queueに残っているもののcostがすべて記憶したcost以上になった時点で終了。
  // Queueがカラになったら-1。
  let mut h = BinaryHeap::new();
  // LeftとRightで、Leftのほうが長いかどうか。
  let mut isLeftLong = false;
  let mut cost = 0i64;
  // まず各Sについて、Leftだけで成立しているかどうかをチェック。
  let mut ans = std::i64::MAX;
  for (s, c) in SC.iter() {
    if check2(&s[..]) {
      ans = min(ans, *c);
    } else {
      h.push((Reverse(*c), s.clone(), true));
    }
  }
  // heapの中身についてチェック。
  while let Some((Reverse(c), s, isLeftLong)) = h.pop() {
    if c >= ans {
      break;
    }
    debug!(c, s, isLeftLong);
    if isLeftLong {
      // Rightを追加してチェック。
      for (r, cc) in RC.iter() {
        match check(&s, r) {
          't' => ans = min(ans, c + *cc),
          'f' => continue,
          _ => {
            if (*s).len() > (*r).len() {
              h.push((Reverse(c + *cc), s[(*r).len()..].to_vec(), true))
            } else {
              h.push((Reverse(c + *cc), r[(*s).len()..].to_vec(), false))
            }
          }
        }
      }
    } else {
      // Leftを追加してチェック。
      for (l, cc) in SC.iter() {
        match check(&s, l) {
          't' => ans = min(ans, c + *cc),
          'f' => continue,
          _ => {
            if (*l).len() > (*s).len() {
              h.push((Reverse(c + *cc), l[(*s).len()..].to_vec(), true))
            } else {
              h.push((Reverse(c + *cc), s[(*l).len()..].to_vec(), false))
            }
          }
        }
      }
    }
  }
  if ans == std::i64::MAX {
    println!("-1");
  } else {
    println!("{}", ans);
  }
}
// bool=trueの時、0:equal, -1:OK, but left is longer, 1: OK, but right is longer
// bool=falseならその時点で不成立。
fn check(left: &Vec<char>, right: &Vec<char>) -> char {
  if left.len() > right.len() {
    check(right, left)
  } else if left[..] != right[..left.len()] {
    'f'
  } else if check2(&right[left.len()..]) {
    't'
  } else {
    'u'
  }
}
fn check2(s: &[char]) -> bool {
  let l = (*s).len() / 2;
  let mut t = (*s).to_vec().clone();
  t.reverse();
  s[..l] == t[..l]
}