My solution first divides people into their families. I build up a list
of santas (where each person gives to the next in the list, and the tail
gives to the head) by:
1) For the first santa, choose from the family with the most people.
2) For all other santas, choose from the family with the most remaining
people that is not the family of the last santa.
I wasn't able to think of any situations that would thwart this strategy.
I, too, just print out the names at the console instead of e-mailing.
And there is also nothing random about my strategy, so given the same
input you will always get the same output. It would be a simple matter
to shuffle the members within the families though; that would mix things
up a little.
Note to other submitters, the following is a good edge case to test:
Luke1 Skywalker <luke / theforce.net>
Luke2 Skywalker <luke / theforce.net>
Luke3 Skywalker <luke / theforce.net>
Luke4 Skywalker <luke / theforce.net>
Leia Skywalker <leia / therebellion.org>
Toula Portokalos <toula / manhunter.org>
Gus Portokalos <gus / weareallfruit.net>
Bruce Wayne <bruce / imbatman.com>
Virgil Brigman <virgil / rigworkersunion.org>
---
# Represents a family.
class Family
attr_reader :name, :members
def initialize(name)
@name = name
@members = []
end
# Number of people in family.
def count
@members.length
end
# Pop the last member off.
def pop
@members.pop
end
# Compare by size.
def <=>(other)
count <=> other.count
end
end
class Person
attr_reader :first_name, :last_name, :email
def initialize(first_name, last_name, email)
@first_name = first_name
@last_name = last_name
@email = email
end
def to_s
"#{@first_name} #{@last_name} <#{@email}>"
end
end
familyTable = Hash.new {|h,k| h[k] = Family.new(k)}
while line = gets
line =~ /(\w+) (\w+) <(.+)>/
first, last, email = $1, $2, $3
familyTable[last].members << Person.new(first, last, email)
end
puts
puts "Processing..."
families = familyTable.values
santas = []
while families.length > 0
families.sort!.reverse!
if families.first.count == 0
# nobody is left; we're done
break
end
if santas.length == 0
# for the very first santa, choose someone from
# the largest family
santas << families.first.pop
else
success = false
# find largest family that is not one's own
families.each do |family|
if family.name != santas.last.last_name
santas << family.pop
success = santas.last
break
end
end
raise "No solution." unless success
end
end
puts "Success!"
puts
lastSanta = santas.last
santas.each do |santa|
puts santa.to_s + " => " + lastSanta.to_s
lastSanta = santa
end