The problem is simple: you are given a grid of numbers, some positive and some negative. The goal is to minimize the sum of the grid, while keeping the sums of the individual rows and columns positive. The only operation that can be performed is flipping the sign of the individual rows of columns. For instance, given the grid:
[ 2 -3 ] -1 [ -3 5 ] 2 -1 2 2
A flip to the first row and the first column would result in the grid:
[ -2 3 ] 1 [ -3 5 ] 2 # After flipping the first row -5 8 6 [ 2 3 ] 5 [ 3 5 ] 8 # After flipping the first column 5 8 26
This is the best solution for this grid, because it is the only solution where the sums of all the rows and columns are positive.
The solution we came up with for this problem was almost as simple: for every possible combination of flipped/not flipped columns1, make sure that every row is positive. If, after making the sum of every row positive, the sum of every column is positive, the grid is a contender for the smallest sum.
Now, there is one small problem: rows which have a sum of zero. If a row's sum is zero, a flip won't change the value of the row, but it will change the value of the columns. The obvious solution is simply to keep track of which rows have a sum of zero, then try flipping them... And that's exactly what we did.
Now, the biggest downside to this algorithm is that it runs in
n is the shortest side of the grid... But in practice, even my less-than-optimal Python code could solve the "large" data set in less than three seconds (Ted's C# code was almost instant).
It makes me wonder, though, is there any way to speed this up?
See /code/facebook/gridflip/ for source code and test grids.
1: Columns for simplicity -- the actual code makes sure there are fewer columns than rows by transposing the grid.
Edit: So, it appears that I can't add. I've fixed up the example grids so that they actually make sense.
Edit by Ted: I'd like to try to clarify our algorithm a little since the explanation is crappy.
Let's say we have a 10 x 20 grid. First we chose the smallest dimension which in this example is 10. Then we enumerate all possible ways of the 10 rows which is 2^10 = 1024 ways. For each of those 1024 ways, we look at the 20 columns and update their sums. If any sum is < 0 we flip the column and now its sum is positive. This allows us to directly solve the pattern the columns need to be flipped in that can give a valid solution.
The only issue though is when the sum is zero. In this case we don't know whether to flip or not. Maybe out of the 20 columns, 3 will sum to zero (very few sum to zero and it does vary based on permutations but stays quite low) so that is 2^3 which is 8 possibilities. In the end the sign of the sums is checked and it is determined if we are the max sum. Only a few thousand possibilities and the operations during the iteration are quite simple (adding a few numbers and so if-else's). This results in a very fast runtime.