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

use std::cmp::{min, Reverse};
use std::collections::{BTreeMap, BinaryHeap};

const INF: i128 = 1e30 as i128;

fn main() {
    let (r, w) = (std::io::stdin(), std::io::stdout());
    let mut sc = IO::new(r.lock(), w.lock());

    let n: usize = sc.read();
    let mut dp = BTreeMap::new();
    let mut heap = BinaryHeap::new();

    for _ in 0..n {
        let x = sc.chars();
        let cost = sc.read();

        let cur = dp.entry(x.clone()).or_insert(INF);
        if *cur > cost {
            *cur = cost;
            heap.push((Reverse(cost), x));
        }
    }

    let s = dp.iter().map(|(a, b)| (a.clone(), *b)).collect::<Vec<_>>();

    while let Some((_, search)) = heap.pop() {
        let cur_cost = dp[&search];
        if is_reversible(&search) {
            continue;
        }

        for (seg, cost) in s
            .iter()
            .map(|s| (s.0.iter().cloned().rev().collect::<Vec<_>>(), s.1))
        {
            let next_cost = cost + cur_cost;
            let prefix_length = prefix_length(&seg, &search);

            if prefix_length == seg.len() && prefix_length == search.len() {
                let cur = dp.entry(vec![]).or_insert(INF);
                if *cur > next_cost {
                    *cur = next_cost;
                    heap.push((Reverse(next_cost), vec![]));
                }
            } else if prefix_length == search.len() {
                let mut overflow = seg[prefix_length..].to_vec();
                overflow.reverse();

                let cur = dp.entry(overflow.clone()).or_insert(INF);
                if *cur > next_cost {
                    *cur = next_cost;
                    heap.push((Reverse(next_cost), overflow));
                }
            } else if prefix_length == seg.len() {
                let remain = search[prefix_length..].to_vec();

                let cur = dp.entry(remain.clone()).or_insert(INF);
                if *cur > next_cost {
                    *cur = next_cost;
                    heap.push((Reverse(next_cost), remain));
                }
            }
        }
    }

    let mut ans = INF;
    for (s, v) in dp.into_iter() {
        if is_reversible(&s) {
            ans = min(ans, v);
        }
    }

    if ans == INF {
        println!("-1");
    } else {
        println!("{}", ans);
    }
}

fn prefix_length<T: PartialEq>(s: &[T], t: &[T]) -> usize {
    for i in 0.. {
        if i >= s.len() || i >= t.len() || s[i] != t[i] {
            return i;
        }
    }
    unreachable!()
}

fn is_reversible<T: PartialEq>(a: &[T]) -> bool {
    let n = a.len();
    for i in 0..n {
        if a[i] != a[n - 1 - i] {
            return false;
        }
    }
    true
}

pub struct IO<R, W: std::io::Write>(R, std::io::BufWriter<W>);

impl<R: std::io::Read, W: std::io::Write> IO<R, W> {
    pub fn new(r: R, w: W) -> Self {
        Self(r, std::io::BufWriter::new(w))
    }
    pub fn write<S: ToString>(&mut self, s: S) {
        use std::io::Write;
        self.1.write_all(s.to_string().as_bytes()).unwrap();
    }
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .0
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r' || b == b'\t')
            .take_while(|&b| b != b' ' && b != b'\n' && b != b'\r' && b != b'\t')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}