// Arup Guha
// 6/30/2013
// Solution to 2012 MCPC Problem E: The Mark of a Wizard

// Note: This is a really clunky, inelegant solution...I get min and max distances between all pairs of points.
//       Then, I try to see if each subset of vertices works for placing markers. I maintain a list of "safe"
//       vertices and then try to add to that list. Safe vertices are ones where all paths from them lead to
//       the opening with minimum distance. When we add to the list, or goal is to use the markers to find
//       one edge from a non-safe edge that leads to a safe vertex with minimum distance for the whole path.
//       If we can do this, then this vertex is safe. This, in turn leads to other safe vertices. Then we repeat.
//       After we are done, we can see if the start vertex is in our set, or if all vertices it leads to directly
//       are safe and lead to a minimal distance.

import java.util.*;

public class e {

	public static void main(String[] args) {

		Scanner stdin = new Scanner(System.in);
		int n = stdin.nextInt();

		while (n != 0) {

			// Read in this strange way since input doesn't specify which letters will be used.
			int[][] mat = new int[26][26];
			for (int i=0; i<n; i++) {

				int v1 = stdin.next().charAt(0) - 'A';
				int edges = stdin.nextInt();
				for (int j=0; j<edges; j++) {
					int v2 = stdin.next().charAt(0) - 'A';
					int dist = stdin.nextInt();
					mat[v1][v2] = dist;
				}

			}

			int[][] adj = reduce(mat);

			// Set up Floyd's and reverse, then solve.
			int[][] minDist = getMinDist(adj);
			int[][] maxDist = getMaxDist(adj);
			int end = getEnd(adj);
			int minDistance = minDist[0][end];
			int minArrows = solve(adj, minDist, maxDist, end);

			// Output result.
			System.out.println(minDistance+" "+minArrows);

			// Go to next case.
			n = stdin.nextInt();
		}
	}

	// I solve for the minimum number of markers here.
	public static int solve(int[][] adj, int[][] min, int[][] max, int end) {

		int n = adj.length;
		ArrayList<Integer> safe = new ArrayList<Integer>();
		safe.add(end);

		// Create the full list of vertices for which the shortest and longest distance to the surface are the same.
		for (int i=0; i<n; i++)
			if (min[i][end] == max[i][end])
				safe.add(i);

		// Set up order to try subsets of vertices.
		subset[] masks = new subset[1 << n];
		for (int i=0; i<masks.length; i++)
			masks[i] = new subset(i);
		Arrays.sort(masks);

		// Return the first one that works.
		for (int i=0; i<masks.length; i++) {

			TreeSet<Integer> list = new TreeSet<Integer>();
			for (int j=0; j<safe.size(); j++)
				list.add(safe.get(j));

			if (satisfiable(adj, min, max, list, masks[i], end))
				return masks[i].bits;
		}

		// Should never get here.
		return n-1;
	}

	// Returns true iff the subset s is a satisfiable subset of vertices from which to place markers.
	public static boolean satisfiable(int[][] adj, int[][] min, int[][] max, TreeSet<Integer> safe, subset s, int end) {

		// Get the bits of the subset.
		int n = adj.length;
		ArrayList<Integer> toAdd = s.getBits();

		// Try to add as many items as we can to the safe list.
		while (true) {

			// Get the next item to add to our safe list.
			int item = getSafe(adj, min, safe, toAdd, end);

			if (item == -1) break;

			// Process this item.
			safe.add(item);
			int index = toAdd.indexOf((Object)item);
			toAdd.remove(index);

			// Also add to safe any new items in the whole list that only connect to safe items.
			// The trick here is the transitivity...if we can add one item, we must check to see
			// if we can add others. That is what the outer while loop is doing, going back and
			// trying new items that might have failed, given that a new item has been added into
			// the safe list.
			while (true) {

				boolean added = false;

				// Go through each item.
				for (int i=0; i<n; i++) {

					if (safe.contains(i)) continue;

					// See if this item should be axed, because it can go to a unsafe place or in going to
					// a safe place achieves a non-optimal answer.
					boolean canAdd = true;
					for (int j=0; j<n; j++) {
						if (adj[i][j] > 0 && (adj[i][j] + min[j][end] > min[i][end]))
							canAdd = false;
						if (adj[i][j] > 0 && !safe.contains(j))
							canAdd = false;
					}

					// Add this item!
					if (canAdd) {
						safe.add(i);
						added = true;
					}
				}

				// We can stop processing!
				if (!added) break;
			}
		}

		// Great, this is a safe vertex!
		if (safe.contains(0)) return true;

		// Return false, if there's a "bad" way to go from 0 to end.
		for (int i=0; i<adj.length; i++)
			if (adj[0][i] > 0)
				if (!safe.contains(i) || adj[0][i] + min[i][end] > min[0][end])
					return false;

		// We're okay!
		return true;
	}

	// Returns a safe vertex to add to the set from toAdd, if one exists, -1 otherwise.
	public static int getSafe(int[][] adj, int[][] min, TreeSet<Integer> safe, ArrayList<Integer> toAdd, int end) {

		// Try each item in toAdd as a viable choice.
		for (int i=0; i<toAdd.size(); i++) {

			int v = toAdd.get(i);

			// We found a way to link this vertex to a safe one so that the distance is minimal.
			for (int j=0; j<adj[v].length; j++)
				if (adj[v][j] > 0 && safe.contains(j) && adj[v][j]+min[j][end] == min[v][end])
					return v;
		}

		// Nothing was found.
		return -1;
	}

	// Removes unused rows/columns that have all 0s.
	public static int[][] reduce(int[][] mat) {

		ArrayList<Integer> vList = new ArrayList<Integer>();

		// Make list of valid vertices.
		for (int i=0; i<mat.length; i++) {

			boolean used = false;
			for (int j=0; j<mat[i].length; j++)
				if (mat[i][j] != 0)
					used = true;

			for (int j=0; j<mat.length; j++)
				if (mat[j][i] != 0)
					used = true;

			if (used)
				vList.add(i);
		}

		// Allocate new array.
		int[][] ans = new int[vList.size()][vList.size()];

		// Copy relevant values.
		for (int i=0; i<vList.size(); i++)
			for (int j=0; j<vList.size(); j++)
				ans[i][j] = mat[vList.get(i)][vList.get(j)];

		// This is our reduced matrix.
		return ans;
	}

	public static int[][] getMaxDist(int[][] adj) {

		int n = adj.length;
		int[][] ans = new int[n][n];

		// Use copy so adj is untouched.
		for (int i=0; i<n; i++)
			for (int j=0; j<n; j++)
				ans[i][j] = adj[i][j];

		// This is sort of like opposite Floyd's.
		for (int k=0; k<n; k++) {
			for (int i=0; i<n; i++) {
				for (int j=0; j<n; j++) {
					if (ans[i][k] > 0 && ans[k][j] > 0 && ans[i][k]+ans[k][j] > ans[i][j])
						ans[i][j] = ans[i][k] + ans[k][j];
				}
			}
		}

		return ans;
	}

	// Just returns the last vertex.
	public static int getEnd(int[][] adj) {

		for (int i=0; i<adj.length; i++) {
			boolean ans = true;
			for (int j=0; j<adj[i].length; j++)
				if (adj[i][j] != 0)
					ans = false;
			if (ans) return i;
		}

		// Should never get here.
		return 0;
	}


	public static int[][] getMinDist(int[][] adj) {

		int n = adj.length;
		int[][] ans = new int[n][n];

		// Use copy so adj is untouched.
		for (int i=0; i<n; i++)
			for (int j=0; j<n; j++)
				ans[i][j] = adj[i][j];

		// This is Floyd's.
		for (int k=0; k<n; k++) {
			for (int i=0; i<n; i++) {
				for (int j=0; j<n; j++) {
					if ((ans[i][k] > 0 && ans[k][j] > 0) && (ans[i][k]+ans[k][j] < ans[i][j] || ans[i][j] == 0))
						ans[i][j] = ans[i][k] + ans[k][j];
				}
			}
		}

		return ans;
	}
}

// Allows us to sort subsets in order by size and then lexicographical order.
class subset implements Comparable<subset> {

	public int mask;
	public int bits;

	public subset(int m) {
		mask = m;
		bits = numBits();
	}

	public int compareTo(subset other) {
		if (this.bits != other.bits) return this.bits - other.bits;
		return this.mask - other.mask;
	}

	private int numBits() {
		int n = mask, cnt=0;
		while (n > 0) {
			if ((n & 1) == 1) cnt++;
			n = n >> 1;
		}
		return cnt;
	}

	public ArrayList<Integer> getBits() {

		int n = mask;
		int i = 0;
		ArrayList<Integer> set = new ArrayList<Integer>();
		while (n > 0) {
			if ((n&1) == 1) set.add(i);
			n = n >> 1;
			i++;
		}

		return set;
	}
}