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)

// ---------- begin chmin, chmax ----------
trait ChangeMinMax {
    fn chmin(&mut self, x: Self) -> bool;
    fn chmax(&mut self, x: Self) -> bool;
}

impl<T: PartialOrd> ChangeMinMax for T {
    fn chmin(&mut self, x: Self) -> bool {
        *self > x && {
            *self = x;
            true
        }
    }
    fn chmax(&mut self, x: Self) -> bool {
        *self < x && {
            *self = x;
            true
        }
    }
}
// ---------- end chmin, chmax ----------

use proconio::*;
use proconio::marker::*;
use std::cmp::*;

fn is_palindrome<T: Eq>(s: &[T]) -> bool {
    s.iter().zip(s.iter().rev()).all(|p| *p.0 == *p.1)
}

fn lcp<T: Eq>(a: &[T], b: &[T]) -> usize {
    a.iter().zip(b.iter()).take_while(|p| *p.0 == *p.1).count()
}

fn run() {
    input! {
        n: usize,
        ask: [(Bytes, u64); n],
    }
    let mut a = vec![];
    let mut b = vec![];
    let mut c = vec![];
    for (s, w) in ask {
        a.push(s.clone());
        let mut s = s;
        s.reverse();
        b.push(s);
        c.push(w);
    }
    let w = 21;
    let mut graph = vec![vec![]; n * 2 * w + 1];
    let src = graph.len() - 1;
    for (i, (x, (y, &z))) in a.iter().zip(b.iter().zip(c.iter())).enumerate() {
        if is_palindrome(x) {
            graph[src].push((src, z));
        } else {
            let len = x.len();
            graph[src].push((i * w * 2 + len, z));
            graph[src].push((i * w * 2 + w + len, z));
        }
        for (j, (p, (q, &r))) in a.iter().zip(b.iter().zip(c.iter())).enumerate() {
            for k in 1..=x.len() {
                let (x, y) = (&x[(x.len() - k)..], &y[(y.len() - k)..]);
                let n = lcp(x, q);
                let m = min(x.len(), q.len());
                if m == n {
                    let x = &x[n..];
                    let q = &q[n..];
                    let v = i * w * 2 + k;
                    if is_palindrome(x) && is_palindrome(q) {
                        graph[v].push((src, r));
                    } else if !x.is_empty() {
                        graph[v].push((i * w * 2 + x.len(), r));
                    } else {
                        graph[v].push((j * w * 2 + w + q.len(), r));
                    }
                }
                let n = lcp(y, p);
                let m = min(y.len(), p.len());
                if m == n {
                    let y = &y[n..];
                    let p = &p[n..];
                    let v = i * w * 2 + w + k;
                    if is_palindrome(y) && is_palindrome(p) {
                        graph[v].push((src, r));
                    } else if !y.is_empty() {
                        graph[v].push((i * w * 2 + w + y.len(), r));
                    } else {
                        graph[v].push((j * w * 2 + p.len(), r));
                    }
                }
            }
        }
    }
    let mut dp = vec![std::u64::MAX; graph.len()];
    let mut h = std::collections::BinaryHeap::new();
    for &(u, c) in graph[src].iter() {
        if dp[u].chmin(c) {
            h.push(Reverse((c, u)));
        }
    }
    while let Some(Reverse((d, v))) = h.pop() {
        if dp[v] < d {
            continue;
        }
        for &(u, c) in graph[v].iter() {
            let d = c + d;
            if dp[u].chmin(d) {
                h.push(Reverse((d, u)));
            }
        }
    }
    if dp[src] != std::u64::MAX {
        println!("{}", dp[src]);
    } else {
        println!("-1");
    }
}

fn main() {
    run();
}