#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define DEBUG
#ifdef DEBUG
#define debug(...) printf( __VA_ARGS__)
#else
#define debug(...)
#endif
#define N 27
#define MAX_INT 20000000
using namespace std;
struct eg{
char x;
char y;
int w;
};
struct eg edge[75];
char parent[N]; /* MF Set, 树根的parent为负数,其绝对值表示这棵树的高度 */
int n, m;
/* 找i的根节点 */
int find(int i)
{
for(; parent[i] >= 0; i = parent[i]) ;
return i;
}
void merge(int x,int y, int px, int py)
{
if (px == py) return;
debug("%d的树根为%d,树高=%d,%d的树根为%d, 树高=%d\n", x, px, -parent[px], y, py, -parent[py]);
if (parent[px] < parent[py]) { /* x所在的树比y所在的树要高 */
parent[py] = px;
debug("合并后树根为%d, 高度=%d\n", px, -parent[px]);
}
else if (parent[px] > parent[py]) { /* x所在的树比y所在的树要矮 */
parent[px] = py;
debug("合并后树根为%d, 高度=%d\n", py, -parent[py]);
}
else {
parent[py] = px;
parent[px]--; /* 树的高度加1 */
debug("合并后树根为%d, 高度=%d\n", px, -parent[px]);
}
}
static int
compare(const void *p1, const void *p2)
{
return ((struct eg *)p1)->w - ((struct eg *)p2)->w;
}
int kruskal()
{
int i, total_cost;
int px, py;
total_cost = 0;
qsort(edge, m, sizeof(eg), compare); /* 按边的长度从小到大排序 */
memset(parent, -1, sizeof(parent));
for (i = 0; i < m; i++) {
debug("%c-%c : %d\n", edge[i].x+'A', edge[i].y + 'A', edge[i].w);
}
for (i = 0; i < m; i++) {
px = find(edge[i].x);
py = find(edge[i].y);
if (px != py) { /* 这条边的两个顶点不在同一个集合中 */
merge(edge[i].x, edge[i].y, px, py);
debug("边%c-%c加入集合, 长度为%d\n", edge[i].x+'A', edge[i].y+'A', edge[i].w);
total_cost += edge[i].w;
}
}
return total_cost;
}
int main()
{
int i, j, w;
char ch;
char adj;
while (cin >> n && n) {
m = 0;
for (i = 1; i < n; i++) {
cin >> ch >> j;
while (j--) {
cin >> adj >> w;
edge[m].x = ch-'A';
edge[m].y = adj-'A';
edge[m].w = w;
m++;
}
}
debug("总共有%d条边\n", m);
printf("%d\n", kruskal());
}
return 0;
}