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 Scala (2.13.1) (judge verdict: Accepted)

import scala.collection.mutable

object Main extends App {
  val sc = new java.util.Scanner(System.in)
  val n = sc.nextInt()
  val scs = Array.fill(n)((sc.next(), sc.nextLong()))

  var visited = Set[(String, Boolean)](("", true))
  var ts = List[((String, Boolean), (String, Boolean), Long)]()
  val queue = mutable.Queue(("", true))
  while (queue.nonEmpty) {
    val now @ (r, front) = queue.dequeue()
    for ((s, c) <- scs) {
      val s1 = if (front) s else s.reverse
      if (r.length <= s1.length) {
        if (s1.startsWith(r)) {
          val next = (s1.drop(r.length), (s1 == r) || !front)
          ts ::= (now, next, c)
          if (!visited.contains(next)) {
            visited += next
            queue.enqueue(next)
          }
        }
      } else {
        if (r.startsWith(s1)) {
          val next = (r.drop(s1.length), front)
          ts ::= (now, next, c)
          if (!visited.contains(next)) {
            visited += next
            queue.enqueue(next)
          }
        }
      }
    }
  }

  val renameMap = visited.zipWithIndex.toMap
  val edges = ts.map{case (s1, s2, c) => (renameMap(s1), renameMap(s2), c)}

  val sp = shortestPath(renameMap(("", true)), visited.size, edges)
  var ans = renameMap.collect{case ((s, _), m) if s == s.reverse => sp(m)}.filter(_ != -1).minOption.getOrElse(-1L)
  println(ans)

  def shortestPath(s: Int, n: Int, edges: Seq[(Int, Int, Long)]): Array[Long] = {
    val adj = Array.fill(n)(List[(Int, Long)]())
    for ((u, v, l) <- edges) {
      adj(u) ::= (v, l)
    }

    val sp = Array.fill(n)(-1L)
    val heap = new BinaryHeap[Long](n, _ < _)
    heap.add(s, 0)
    while (heap.nonEmpty) {
      val (u, spu) = heap.pop()
      for ((v, l) <- adj(u)) {
        if (sp(v) == -1) {
          sp(v) = spu + l
          heap.add(v, sp(v))
        } else if (spu + l < sp(v)) {
          sp(v) = spu + l
          heap.upPriority(v, sp(v))
        }
      }
    }
    sp
  }

  class BinaryHeap[P](maxSize: Int, comparator: (P, P) => Boolean) {
    private val heap = Array.ofDim[(Int, P)](maxSize)
    private val index = Array.fill(maxSize)(-1)

    var size = 0

    def isEmpty: Boolean = size == 0
    def nonEmpty: Boolean = !isEmpty

    private def par(idx: Int): Int = (idx - 1) / 2
    private def left(idx: Int): Int = 2 * idx + 1
    private def right(idx: Int): Int = 2 * idx + 2

    private def isRoot(idx: Int): Boolean = idx == 0
    private def hasLeft(idx: Int): Boolean = left(idx) < size
    private def hasRight(idx: Int): Boolean = right(idx) < size

    private def swap(i: Int, j: Int): Unit = {
      val t = heap(i)
      heap(i) = heap(j)
      heap(j) = t
      index(heap(i)._1) = i
      index(heap(j)._1) = j
    }

    private def upHeap(idx: Int): Unit = {
      if (!isRoot(idx) && comparator(heap(idx)._2, heap(par(idx))._2)) {
        swap(idx, par(idx))
        upHeap(par(idx))
      }
    }

    private def downHeap(idx: Int): Unit = {
      if (hasLeft(idx)) {
        val t = if (hasRight(idx) && comparator(heap(right(idx))._2, heap(left(idx))._2)) {
          right(idx)
        } else {
          left(idx)
        }
        if (comparator(heap(t)._2, heap(idx)._2)) {
          swap(idx, t)
          downHeap(t)
        }
      }
    }

    def add(a: Int, p: P): Unit = {
      size += 1
      heap(size - 1) = (a, p)
      index(a) = size - 1
      upHeap(size - 1)
    }

    def pop(): (Int, P) = {
      val head = heap(0)
      heap(0) = heap(size - 1)
      size -= 1
      downHeap(0)
      head
    }

    def upPriority(a: Int, p: P): Unit = {
      val idx = index(a)
      heap(idx) = (a, p)
      upHeap(idx)
    }
  }
}