What you're trying to do is find the vector in the solution space with the minimum Hamming weight. Unfortunately, this problem is equivalent to the minimum distance problem in coding theory, which has been proven to be NP-hard (http://people.math.umass.edu/~hajir/m499c/vardy-complexity.pdf). That said, the implementation you link to can be sped up greatly by using ints as bit arrays and using bitwise operations, effectively parallelizing many of the operations.
Here's the quick-and-dirty Java solution I wrote for the Google Foobar challenge, which runs reasonably quickly and works for n <= 15.
public class GridZero
{
public static int[][] returnn3()
{
final int[][] n3={{284}, {149}, {78}, {63}};
return n3;
}
public static int[][] returnn5()
{
final int[][] n5= {{17284592}, {8659223}, {4329627}, {2164829}, {1082430}, {1015839}, {31775}, {1023}};
return n5;
}
public static int[][] returnn7()
{
final int[][] n7={{-2130838537, 122816}, {1082196484, 4191}, {541098242, 2159}, {270549121, 1143}, {135274560, 66171}, {67637280, 33149}, {33818640, 16638}, {33292288, 127}, {260096, 127}, {2032, 127}, {15, 114815}, {0, 16383}};
return n7;
}
public static int[][] returnn9()
{
final int[][] n9={{-2143297553, -134480386, 130816}, {1075843080, 67240192,
65919}, {537921540, 33620096, 33215}, {268960770, 16810048,
16863}, {134480385, 8405024, 8687}, {67240192, -2143281136,
4599}, {33620096, 1075843080, 2555}, {16810048, 537921540,
1533}, {8405024, 268960770, 1022}, {8372224, 0, 511}, {16352, 0,
511}, {31, -268435456, 511}, {0, 267911168, 511}, {0, 523264,
511}, {0, 1022, 511}, {0, 1, 131071}};
return n9;
}
public static int[][] returnn11()
{
final int[][] n11= {{-2146435585, -1074266369, -537133185, 31456256}, {1074266368,
537133184, 268566592, 1050111}, {537133184, 268566592, 134283296,
526079}, {268566592, 134283296, 67141648, 264063}, {134283296,
67141648, 33570824, 133055}, {67141648, 33570824, 16785412,
67551}, {33570824, 16785412, 8392706, 34799}, {16785412, 8392706,
4196353, 18423}, {8392706, 4196353, 2098176, 16787451}, {4196353,
2098176, -2146434560, 8394749}, {2098176, -2146434560, 1074266368,
4198398}, {2096128, 0, 0, 2047}, {1023, -2147483648, 0, 2047}, {0,
2146435072, 0, 2047}, {0, 1048064, 0, 2047}, {0, 511, -1073741824,
2047}, {0, 0, 1073217536, 2047}, {0, 0, 524032, 2047}, {0, 0, 255,
29362175}, {0, 0, 0, 4194303}};
return n11;
}
public static int[][] returnn13()
{
final int[][] n13={{-2147221537, -16779265, -1073872913, -8389633, -536936456,
0}, {1073872912, 8389632, 536936456, 4194816, 268468235,
511}, {536936456, 4194816, 268468228, 2097408, 134234125,
511}, {268468228, 2097408, 134234114, 1048704, 67117070,
511}, {134234114, 1048704, 67117057, 524352, 33558543,
255}, {67117057, 524352, 33558528, -2147221472, 16779279,
383}, {33558528, -2147221472, 16779264, 1073872912, 8389647,
447}, {16779264, 1073872912, 8389632, 536936456, 4194831,
479}, {8389632, 536936456, 4194816, 268468228, 2097423,
495}, {4194816, 268468228, 2097408, 134234114, 1048719,
503}, {2097408, 134234114, 1048704, 67117057, 524367,
507}, {1048704, 67117057, 524352, 33558528, -2147221457,
509}, {524352, 33558528, -2147221472, 16779264, 1073872927,
510}, {524224, 0, 0, 0, 15, 511}, {63, -33554432, 0, 0, 15,
511}, {0, 33550336, 0, 0, 15, 511}, {0, 4095, -2147483648, 0, 15,
511}, {0, 0, 2147221504, 0, 15, 511}, {0, 0, 262112, 0, 15,
511}, {0, 0, 31, -16777216, 15, 511}, {0, 0, 0, 16775168, 15,
511}, {0, 0, 0, 2047, -1073741809, 511}, {0, 0, 0, 0, 1073610767,
511}, {0, 0, 0, 0, 131071, 511}};
return n13;
}
public static int[][] returnn15()
{
final int[][] n15={{-2147418115, -262153, -1048609, -4194433, -16777729, -67110913,
-268443648, 0}, {1073774593, 131076, 524304, 2097216, 8388864,
33555456, 134230015, 1}, {536887296, -2147418110, 262152, 1048608,
4194432, 16777728, 67123199, 1}, {268443648, 1073774593, 131076,
524304, 2097216, 8388864, 33569791, 1}, {134221824,
536887296, -2147418110, 262152, 1048608, 4194432, 16793087,
1}, {67110912, 268443648, 1073774593, 131076, 524304, 2097216,
8404735, 1}, {33555456, 134221824, 536887296, -2147418110, 262152,
1048608, 4210559, 1}, {16777728, 67110912, 268443648, 1073774593,
131076, 524304, 2113471, 1}, {8388864, 33555456, 134221824,
536887296, -2147418110, 262152, 1064927, 1}, {4194432, 16777728,
67110912, 268443648, 1073774593, 131076, 540655, 1}, {2097216,
8388864, 33555456, 134221824, 536887296, -2147418110, 278519,
1}, {1048608, 4194432, 16777728, 67110912, 268443648, 1073774593,
147451, 1}, {524304, 2097216, 8388864, 33555456, 134221824,
536887296, -2147401731, 1}, {262152, 1048608, 4194432, 16777728,
67110912, 268443648, 1073790974, 1}, {131076, 524304, 2097216,
8388864, 33555456, 134221824, 536903679, 0}, {131068, 0, 0, 0, 0, 0,
16383, 1}, {3, -524288, 0, 0, 0, 0, 16383, 1}, {0, 524272, 0, 0, 0,
0, 16383, 1}, {0, 15, -2097152, 0, 0, 0, 16383, 1}, {0, 0, 2097088,
0, 0, 0, 16383, 1}, {0, 0, 63, -8388608, 0, 0, 16383, 1}, {0, 0, 0,
8388352, 0, 0, 16383, 1}, {0, 0, 0, 255, -33554432, 0, 16383,
1}, {0, 0, 0, 0, 33553408, 0, 16383, 1}, {0, 0, 0, 0,
1023, -134217728, 16383, 1}, {0, 0, 0, 0, 0, 134213632, 16383,
1}, {0, 0, 0, 0, 0, 4095, -536854529, 1}, {0, 0, 0, 0, 0, 0,
536870911, 1}};
return n15;
}
public static int answer(int[][] matrix)
{
int[][] tm = toggleGen(matrix.length,matrix);
int[][] mat = rref(tm);
int zeroRow = mat.length;
for (int i = mat.length - 1; i >=0; i--)
{
int status = 0;
for (int j = 0; j < mat[0].length - 1; j++)
{
if (mat[i][j] == 1)
{
status = 1;
break;
}
}
if (status == 1)
{
zeroRow = i+1;
break;
}
else if (mat[i][mat[0].length-1] == 0) continue;
else return -1;
}
int offset = 0;
int changecheck = 0;
int zeroRowcopy = zeroRow;
for (int r = 0; r < zeroRowcopy; r++)
{
if (mat[r][r+offset] == 1) continue;
else
{
changecheck = 1;
mat[zeroRow++][r + offset] = 1;
offset++;
}
}
if (changecheck == 1) mat = rref(mat);
int len = mat.length/32 + 1;
if (mat.length % 32 == 0) len--;
final int[] sol = new int[len];
int weight = 0;
for (int i = 0; i < mat.length; i++)
{
int val = mat[i][mat[0].length-1];
sol[i/32] = (sol[i/32] << 1) | val;
weight += val;
}
if (matrix.length % 2 == 0) return weight;
final int[][] kernel;
switch (matrix.length)
{
case 3: kernel = returnn3();
break;
case 5: kernel = returnn5();
break;
case 7: kernel = returnn7();
break;
case 9: kernel = returnn9();
break;
case 11: kernel = returnn11();
break;
case 13: kernel = returnn13();
break;
case 15: kernel = returnn15();
break;
default:
kernel = returnn3();
}
int max = 1 << (2*(matrix.length-1));
int minweight = weight;
int res[] = new int[sol.length];
for (int i = 1; i < max; i++)
{
int hamming = basisweighter(kernel,i,sol,res);
if (hamming < minweight)
{
minweight = hamming;
}
}
return minweight;
}
public static int basisweighter(final int[][] kernel, int weights, final int[] sol, int[] res)
{
int changes = weights ^ (weights-1);
int changeint;
int i = 0;
while ((changeint = (changes >> i)) > 0)
{
if ((changeint & 1) == 1)
{
for (int k = 0; k < res.length; k++)
{
res[k] ^= kernel[i][k];
}
}
i++;
}
int weight = 0;
for (int j = 0; j < sol.length; j++)
{
weight += NumberOfSetBits(res[j]^sol[j]);
}
return weight;
}
public static int NumberOfSetBits(int i)
{
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
return (((i + (i >>> 4)) & 0x0F0F0F0F) * 0x01010101) >>> 24;
}
public static int[][] toggleGen(int n, int[][] mat)
{
int[][] ret = new int[n*n][n*n+1];
for (int i = 0; i < n*n; i++)
{
int[] row = new int[n*n+1];
for (int j = 0; j <= n*n; j++)
{
if (j == n*n)
row[j] = mat[i/n][i%n];
else if (i/n == j/n)
row[j] = 1;
else if (i%n == j%n)
row[j] = 1;
else
row[j] = 0;
}
ret[i] = row;
}
return ret;
}
public static int[][] rref(int[][] mat)
{
int[][] rref = new int[mat.length][mat[0].length];
int startRow = 0;
for (int r = 0; r < rref.length; ++r)
{
for (int c = 0; c < rref[r].length; ++c)
{
rref[r][c] = mat[r][c];
}
}
for (int c = 0; c < rref[0].length; ++c)
{
int pivotRow = -1;
for (int r = startRow; r < rref.length; ++r)
{
if (rref[r][c] == 1)
{
pivotRow = r;
break;
}
}
if (pivotRow == -1) continue;
if (pivotRow != startRow)
{
int[] temp = rref[pivotRow];
rref[pivotRow] = rref[startRow];
rref[startRow] = temp;
}
for (int r = 0; r < rref.length; r++)
{
if (r == startRow) continue;
if(rref[r][c] == 1)
{
for (int i = 0; i < rref[0].length; i++)
{
rref[r][i] ^= rref[startRow][i];
}
}
}
startRow++;
}
return rref;
}
}
Best Answer
First, consider the $n\times n$ case.
I claim the following:
Proof:
Case I: $n$ is even. You can toggle just a particular light by toggling all the lights in its row and column. So you can switch off all the lights by going after individual lights.
Case II: $n$ is odd.
Notice that if $n$ is odd, on any single operation, all the row and column parities change simultaneously.
Thus if they are not all the same, we can never achieve all lights off. This shows the necessity of the parities being the same.
To prove sufficiency, consider an arrangement of $(2k+1)\times(2k+1)$ which has all row and column parities the same.
Consider the $2k\times 2k$ subgrid which does not include the bottom row and the right column. Use the above even $n$ case algorithm to switch off all the lights in that $2k\times 2k$ grid. Since the row and column parities all flip together and were initially all the same, we must have that the remaining lights, in the bottom row and right column, are all the same (including the bottom right corner). Now we toggle the bottom right corner, if needed.
$\circ$
Now, the above can be generalized to the $m \times n$ case, when $m$ and $n$ have the same parity.
If $m$ and $n$ have different parities, there is more work to be done.