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 Kotlin (1.3.71) (judge verdict: Accepted)

import java.util.*
import kotlin.math.absoluteValue
import kotlin.math.max
import kotlin.math.min

data class Word(val string: String, val cost: Int)

sealed class State
data class Forward(val wordId: Int, val pos: Int): State()
data class Backward(val wordId: Int, val pos: Int): State()
fun main() {
    val n = readLine()!!.trim().toInt()
    val words = Array(n){
        val (s, c) = readLine()!!.trim().split(' ')
        Word(s, c.toInt())
    }
    val forward = words.map(Word::string)
    val backward = words.map{it.string.reversed()}
    val forwardNext = forward.map{word ->
        word.indices.map{pos ->
            val prefix = word.substring(pos, word.length)
            backward.indices.filter{b -> backward[b].startsWith(prefix) || prefix.startsWith(backward[b])}.toIntArray()
        }
    }
    val backwardNext = backward.map{word ->
        word.indices.map{pos ->
            val prefix = word.substring(pos, word.length)
            forward.indices.filter{f -> forward[f].startsWith(prefix) || prefix.startsWith(forward[f])}.toIntArray()
        }
    }
    val singleMax = words.filter{it.string == it.string.reversed()}.minBy(Word::cost)?.cost?.toLong() ?: Long.MAX_VALUE
    val minCostForward = Array(forward.size){LongArray(forward[it].length + 1){singleMax} }
    val minCostBackward = Array(backward.size){LongArray(backward[it].length + 1){singleMax} }
    val queue = PriorityQueue<Pair<Long, State>>(compareBy(Pair<Long, *>::first))
    for (i in 0 until n) {
        minCostForward[i][0] = words[i].cost.toLong()
        queue.add(words[i].cost.toLong() to Forward(i, 0))
    }
    while (queue.isNotEmpty()) {
        val (cost, state) = queue.poll()
        //println("cost: $cost, state: $state")
        when (state) {
            is Forward -> {
                if (state.pos < forward[state.wordId].length && minCostForward[state.wordId][state.pos] == cost) {
                    val forwardRest = forward[state.wordId].length - state.pos
                    for (next in forwardNext[state.wordId][state.pos]) {
                        if (backward[next].length > forwardRest) {
                            if (minCostBackward[next][forwardRest] > cost + words[next].cost) {
                                minCostBackward[next][forwardRest] = cost + words[next].cost
                                queue.add(cost + words[next].cost to Backward(next, forwardRest))
                            }
                        }else {
                            if (minCostForward[state.wordId][state.pos + backward[next].length] > cost + words[next].cost) {
                                minCostForward[state.wordId][state.pos + backward[next].length] = cost + words[next].cost
                                queue.add(cost + words[next].cost to Forward(state.wordId, state.pos + backward[next].length))
                            }
                        }
                    }
                }
            }
            is Backward -> {
                if (state.pos < backward[state.wordId].length && minCostBackward[state.wordId][state.pos] == cost) {
                    val backwardRest = backward[state.wordId].length - state.pos
                    for (next in backwardNext[state.wordId][state.pos]) {
                        if (forward[next].length > backwardRest) {
                            if (minCostForward[next][backwardRest] > cost + words[next].cost) {
                                minCostForward[next][backwardRest] = cost + words[next].cost
                                queue.add(cost + words[next].cost to Forward(next, backwardRest))
                            }
                        }else {
                            if (minCostBackward[state.wordId][state.pos + forward[next].length] > cost + words[next].cost) {
                                minCostBackward[state.wordId][state.pos + forward[next].length] = cost + words[next].cost
                                queue.add(cost + words[next].cost to Backward(state.wordId, state.pos + forward[next].length))
                            }
                        }
                    }
                }
            }
        }
    }
    val forwardMin = (0 until n).flatMap{i ->
        (0 .. forward[i].length).filter{j -> forward[i].substring(j, forward[i].length) == forward[i].substring(j, forward[i].length).reversed()}.map(minCostForward[i]::get)
    }.min()!!
    val backwardMin = (0 until n).flatMap{i ->
        (0 .. backward[i].length).filter{j -> backward[i].substring(j, backward[i].length) == backward[i].substring(j, backward[i].length).reversed()}.map(minCostBackward[i]::get)
    }.min()!!
    println(min(forwardMin, backwardMin).takeIf{it != Long.MAX_VALUE} ?: -1)
}