// Arup Guha
// 2/15/2015
// Solution to 2007 MCPC Problem: Guard

import java.util.*;

public class g {

	final public static double NO_SOLUTION = 1e9;

	// Input data stored here.
	public static int numPaintings;
	public static int numCorridors;
	public static int numGuards;
	public static pt[] paintings;
	public static int[] corridors;

	// Stores scores for each mask for one guard.
	public static HashMap<Integer,Double> map;

	// Stores what can be seen from each intersection.
	public static int[] canSee;

	// Stores results of recursive calls.
	public static Double[][] dp;

	public static void main(String[] args) {

		Scanner stdin = new Scanner(System.in);
		numPaintings = stdin.nextInt();

		// Go through each case.
		while (numPaintings != 0) {

			numCorridors = stdin.nextInt();
			numGuards = stdin.nextInt();

			// Read in where all the paintings are.
			paintings = new pt[numPaintings];
			for (int i=0; i<numPaintings; i++) {
				String dummy = stdin.next();
				int x = stdin.nextInt();
				int y = stdin.nextInt();
				int val = stdin.nextInt();
				paintings[i] = new pt(x, y, val);
			}

			// Read in the corridors.
			corridors = new int[numCorridors];
			for (int i=0; i<numCorridors; i++)
				corridors[i] = getMask(stdin.next());

			// Get list of where you can see from each painting.
			canSee = new int[numPaintings];
			for (int i=0; i<numPaintings; i++)
				for (int j=0; j<numCorridors; j++)
					if ((corridors[j] & (1 << i)) > 0)
						canSee[i] = canSee[i] | corridors[j];

			// Calculate what we can cover with one guard.
			buildSols();

			// Set up DP array.
			dp = new Double[1 << numPaintings][numGuards+1];
			for (int i=0; i<dp.length; i++)
				Arrays.fill(dp[i], null);
			dp[0][0] = 0.0;

			// Solve and print!
			Double res = solveRec((1 << numPaintings)-1, numGuards);
			if (res != null && res < NO_SOLUTION-1)
				System.out.printf("%.2f\n", res);
			else
				System.out.printf("too few guards\n");

			// Get next case.
			numPaintings = stdin.nextInt();
		}
	}

	// Returns the mask corresponding to s. A = 1, B = 2, C = 4, etc.
	public static int getMask(String s) {
		int mask = 0;
		for (int i=0; i<s.length(); i++)
			mask += (1 << (s.charAt(i)-'A'));
		return mask;
	}

	public static Double solveRec(int mask, int k) {

		if (mask == 0) return 0.0;
		if (k == 0) return NO_SOLUTION;
		if (dp[mask][k] != null) return dp[mask][k];

		Double res = null;

		// Try putting guard k on each possible subset he could watch.
		for (Integer subset: map.keySet()) {

			// Only counts if this subset is within this mask.
			if ((subset & mask) == subset) {

				Double buildOff = solveRec(mask-subset, k-1);
				if (buildOff == null) continue;

				// Cost for using this specific route.
				Double thisCost = Math.max(map.get(subset), buildOff);

				// The cases that warrant an update.
				if (res == null || thisCost.compareTo(res) < 0)
					res = thisCost;
			}
		}

		// Store and return.
		if (res != null) 	dp[mask][k] = res;
		else				dp[mask][k] = NO_SOLUTION;
		return dp[mask][k];
	}

	// Build's answers for one guard and stores in HashMap.
	public static void buildSols() {

		map = new HashMap<Integer,Double>();
		map.put(0, 0.0);

		// Try all masks.
		for (int i=1; i<(1 << numPaintings); i++) {

			boolean assigned = false;

			// First try all on one corridor.
			for (int j=0; j<numCorridors; j++) {
				if ((i | corridors[j]) == corridors[j]) {
					map.put(i,getOneLine(i));
					assigned = true;
					break;
				}
			}

			if (assigned) continue;

			// Try all valid intersections.
			Double res = null;
			for (int j=0; j<numPaintings; j++) {
				if ((i | canSee[j]) == canSee[j]) {
					Double cur = getMultipleLines(i, j);
					if (res == null || cur.compareTo(res) < 0)
						res = cur;
				}
			}

			// Put the best one in the map.
			if (res != null) map.put(i,res);
		}

	}

	public static Double getMultipleLines(int mask, int location) {

		ArrayList<Integer> items = maskList(mask);

		// Try balance point between all pairs of points and take the max.
		Double res = 0.0;
		for (int i=0; i<items.size(); i++) {
			pt one = paintings[items.get(i)];
			pt two = paintings[location];
			res = Math.max(res, one.dist(two)*one.value);
		}

		// This is our result.
		return res;
	}

	// Converts mask to subset.
	public static ArrayList<Integer> maskList(int mask) {
		ArrayList<Integer> items = new ArrayList<Integer>();
		int bit = 0;
		while ( (1<<bit) <= mask) {
			if ((mask & (1 << bit)) > 0)
				items.add(bit);
			bit++;
		}
		return items;
	}

	public static Double getOneLine(int mask) {

		// Can always cover 1 pt without cost.
		if (Integer.bitCount(mask) == 1) return new Double(0.0);

		ArrayList<Integer> items = maskList(mask);

		// Try balance point between all pairs of points and take the max.
		Double res = 0.0;
		for (int i=0; i<items.size(); i++) {
			for (int j=i+1; j<items.size(); j++) {
				pt one = paintings[items.get(i)];
				pt two = paintings[items.get(j)];
				res = Math.max(res, one.dist(two)*one.value*two.value/(one.value+two.value));
			}
		}

		// This is our result.
		return res;
	}
}

class pt {

	int x;
	int y;
	int value;

	public pt(int myx, int myy, int worth) {
		x = myx;
		y = myy;
		value = worth;
	}

	public double dist(pt other) {
		return Math.sqrt(Math.pow(x-other.x,2) + Math.pow(y-other.y,2));
	}

}