Here's my solution - I think I've got it, but haven't found a way to  
prove whether or not it's optimal yet ;-)

-Dustin

# parse input file
# make a hash, key is player, value is array of preferred matches
# in order of preference
preferences = {}
File.open(ARGV[0]) do |file|
   while line = file.gets
     a = line.split
     player = a[0][0...a[0].size-1]
     preferences[player] = a[1...a.size]
   end
end

# score each individual preference, first choice gets 0 score
# i.e. lower scores are better
keys = preferences.keys

matrix = keys.collect { |k| Array.new }
keys.each_with_index do |player, i|
   keys.each_with_index do |oplayer, j|
     next if j < i + 1 # process above main diagonal
     # storing score, which is position of oplayer in preference list
     s1 = preferences[player].index(oplayer)
     s2 = preferences[oplayer].index(player)
     score = s1 + s2
     entry = [player, oplayer, score]
     matrix[i].push(entry)
     matrix[j].push(entry)
   end
end

matrix.size.times do |i|
   matrix[i] = matrix[i].sort { |a,b| a[2] <=> b[2] }
end

matches = []

(matrix.size - 2).downto(1) do |i|
   matrix = matrix.sort { |a,b| b[i][2] <=> a[i][2] }
end

while matrix.size > 0
   matrix = matrix.sort { |a,b| a[0][2] <=> b[0][2] }

   match = matrix.first.first
   matches.push([match[0], match[1]])

   matrix.size.times do |i|
     matrix[i].delete_if do |pmatch|
       pmatch.include?(match[0]) || pmatch.include?(match[1])
     end
   end
   matrix.delete_if { |row| row.empty? }

end

matches.each do |match|
   puts "#{match[0]} #{match[1]}"
end

# find the odd one...
puts keys - matches.flatten.uniq