Refactor: extract a solution struct.
Refactor: shrink the subproblems.

When recursing during the brute force depth-first
search, pass along only the indeterminate indexes,
so that it runs on a smaller subproblem. For this to
work, we had to switch to using a hash, so that it
can handle index gaps.

This provides mixed results: it makes some things faster,
some things slower. I think it's an improvement overall.