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)]
#![allow(unused_macros)]
use std::cmp::{max, min};
use std::collections::*;
use std::io::{stdin, Read};

#[allow(unused_macros)]
macro_rules! parse {
    ($it: ident ) => {};
    ($it: ident, ) => {};
    ($it: ident, $var:ident : $t:tt $($r:tt)*) => {
        let $var = parse_val!($it, $t);
        parse!($it $($r)*);
    };
    ($it: ident, mut $var:ident : $t:tt $($r:tt)*) => {
        let mut $var = parse_val!($it, $t);
        parse!($it $($r)*);
    };
    ($it: ident, $var:ident $($r:tt)*) => {
        let $var = parse_val!($it, usize);
        parse!($it $($r)*);
    };
}

#[allow(unused_macros)]
macro_rules! parse_val {
    ($it: ident, [$t:tt; $len:expr]) => {
        (0..$len).map(|_| parse_val!($it, $t)).collect::<Vec<_>>();
    };
    ($it: ident, ($($t: tt),*)) => {
        ($(parse_val!($it, $t)),*)
    };
    ($it: ident, u1) => {
        $it.next().unwrap().parse::<usize>().unwrap() -1
    };
    ($it: ident, $t: ty) => {
        $it.next().unwrap().parse::<$t>().unwrap()
    };
}

#[cfg(debug_assertions)]
macro_rules! debug {
    ($( $args:expr ),*) => { eprintln!( $( $args ),* ); }
}

#[cfg(not(debug_assertions))]
macro_rules! debug {
    ($( $args:expr ),*) => {
        ()
    };
}

const M: usize = std::usize::MAX / 10;
fn search(
    s: &[String],
    t: &[String],
    n: usize,
    direction: bool,
    curs: String,
    cost: usize,
    c: &[usize],
    seen: &mut HashMap<(bool, String), usize>,
) -> usize {
    let k = (direction, curs.clone());
    if seen.contains_key(&k) && cost >= seen[&k] {
        return M;
    }
    seen.insert(k, cost);
    let mut ret = M;
    for i in 0..n {
        let target = if direction {
            t[i].clone()
        } else {
            s[i].clone()
        };
        let (ns, direction) = if target.starts_with(&curs) {
            (target[curs.len()..].to_string(), !direction)
        } else if curs.starts_with(&target) {
            (curs[target.len()..].to_string(), direction)
        } else {
            continue;
        };
        if ns == ns.chars().rev().collect::<String>() {
            ret = min(ret, cost + c[i]);
        } else if ns.len() < 2 {
            ret = min(ret, cost + c[i]);
        } else {
            ret = min(ret, search(s, t, n, direction, ns, cost + c[i], c, seen));
        }
    }
    ret
}

fn solve(s: &str) {
    let mut it = s.split_whitespace();
    parse!(it, n: usize);
    let mut s = vec![];
    let mut c = vec![];
    for _ in 0..n {
        s.push(it.next().unwrap().to_string());
        parse!(it, c_: usize);
        c.push(c_);
    }
    let t: Vec<_> = s
        .iter()
        .map(|x| x.chars().rev().collect::<String>())
        .collect();
    let mut ret = M;
    for i in 0..n {
        let curs = s[i].clone();
        let mut seen = HashMap::new();
        ret = min(ret, search(&s, &t, n, true, curs, c[i], &c, &mut seen));
        if s[i] == t[i] {
            ret = min(ret, c[i]);
        }
    }

    if ret == M {
        println!("{}", -1);
    } else {
        println!("{}", ret);
    }
}

fn main() {
    let mut s = String::new();
    stdin().read_to_string(&mut s).unwrap();
    solve(&s);
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_input() {
        let s = "
        ";
        solve(s);
    }
}