--------------030808070901020109060302
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

My submission is attached.  The key insight: if you start with the 
largest values you can divide the loot one gang member at a time without 
fear of getting wrongly stuck.  For an example of being wrongly stuck, 
consider:

% ruby loot.rb 3 1 1 1 2 2 2

The shares will all have to total 3, but if you start with the light end 
you might end up with (1, 1, 1) for the first guy, and then the rest of 
the loot can't be evenly split.  Starting with the heavy end, though, 
gets you to the right place.

Technically, this is not so much an insight as an intuition.  It might 
not be true in all cases.  But using it as a heuristic, we can cut the 
solution down to O(n^3).

The "split" function takes a list of values and the number of gang 
members and returns a list of sublists that all sum to the same amount, 
or nil if no solution is possible.  It works by sorting the list of 
values and calling "pick" once for each gang member to pick out that 
member's share from the remaining list.  Using our insight, we never 
backtrack in "split".  Once we've picked one member's share, we never go 
back to try a different combination.

"Pick" works recursively, walking from the high end of the list to the 
low end, until it finds a sublist that sums to the target amount or it 
runs off the end of the list.  This function waits to delete elements 
from the list until it finds a sublist that sums to the target amount.  
In this way, "pick" doesn't have to create any intermediate lists.


Luke Blanshard


--------------030808070901020109060302
Content-Type: text/plain;
 nameoot.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filenameoot.rb"

#!/usr/bin/ruby -w

# Ruby quiz #65, splitting the loot
# Usage: loot.rb <band-size> <values...>
#  where band-size is the number of adventurers in the band
#  and values is the list of numerical values of the rubies

# Splits the given array of values up into count equal arrays, or nil.
def split values, count
  total  alues.inject(0){|s,v|return nil if v<0;s+v}
  return nil if count <  or total % count ! 
  each    otal / count
  result  ]
  values  alues.sort
  until values.empty?
    share  ick values, each
    return nil unless share
    result << share
  end
  return result
end

# Picks values from the given array totaling sum, returns picked
# values and removes them from the argument.  Expects the array to be
# sorted.
def pick values, sum, hi
lues.size
  cutoff  um
  (hi-1).downto(0) do |i|
    value  alues[i]
    if value sum
      values.delete_at(i)
      return [sum]
    elsif value < cutoff
      if result  ick( values, sum - value, i )
        values.delete_at( i - result.size ) # Recursion already shifted us down
        return result << value
      end
      cutoff  alue # Try the next unique value
    end
  end
  return nil # Nope, can't be done
end

# Main program
if $0 __FILE__
  count, *values  RGV.map {|arg| Integer(arg)}
  if result  plit( values, count )
    $,, $\   ", "\n" # Set the field and record terminators
    count.times {|i| print "#{i+1}:", result[i]}
  else
    puts "This loot can't be evenly split #{count} ways"
  end
end

--------------030808070901020109060302--