Fixed my solution to check for tail and head being in same family, and
also add a sorting "bonus" to the family of the head to ensure that
condition doesn't arise if it doesn't have to.
---
# Represents a family.
class Family
attr_reader :name, :members
def initialize(name)
@name = name
@members = []
@bonus = 0
end
# Number of people in family.
def count
@members.length
end
# Give a sorting bonus--a family with
# the bonus will always appear before
# any families with the same count but
# no bonus
def give_bonus
@bonus = 0.1
end
# The count with sorting bonus included
def count_with_bonus
count + @bonus
end
# Pop the last member off.
def pop
@members.pop
end
# Compare by count/bonus.
def <=>(other)
count_with_bonus <=> other.count_with_bonus
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
families.first.give_bonus
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
if santas.length > 0 && santas.first.last_name == santas.last.last_name
raise "No solution."
end
puts "Success!"
puts
lastSanta = santas.last
santas.each do |santa|
puts santa.to_s + " => " + lastSanta.to_s
lastSanta = santa
end