After participating in Google Code Jam 2012 Round 2, it occurred to me that the Mountain View problem boiled down to solving a set of linear inequalities, which could be done in Mathematica. I've only tinkered with Mathematica programming before, but I tried it out to see how hard it was for this problem. I got it to work (for the "small" input set only) with a lot of effort. I felt like I must be doing some things the hard way and would like feedback on the proper Mathematica idioms for file I/O and expression building (and anything else).
You may have to read the problem description (link above), but the basics are:
- You read test cases from an input file, C.in.
- You write answers to an output file, C.out.
- Each input case contains a number of mountains and a constraint number for each mountain.
- Each output line contains a list of possible mountain heights or the word "Impossible"
My program follows.
During development, I send output to stdout. debug
toggles that. I put it early because I thought I might use it for Print
statements.
debug = False;
This function solves a single case, including reading input and writing output.
solveCase[in_, out_, cn_] :=
Module[{nm, im, highest, vars, constraints, i, j, k, rhs, lhs, yk,
yi, yj, answer},
Read the input: the number of mountains and the apparent highest mountain that can be seen from each of the first nm-1 mountains.
nm = Read[in, Number];
highest = Array[0, nm - 1];
For[im = 1, im <= nm - 1, im++,
highest[[im]] = Read[in, Number];
];
Start setting up expressions to pass later to FindInstance
. Is there a way to solve for a vector of variables without giving each one a separate name? Not finding any, I create the variables as h1
, h2
, ... and the initial constraints as h1 >= 0
, h2 >= 0
, ...
vars =
Function[x, ToExpression["h" <> ToString[x]]] /@ Range[nm];
constraints =
Function[x, ToExpression["h" <> ToString[x]] >= 0] /@ Range[nm];
Convert the highest
info into a list of constraints. When mountain i
sees mountain k
as the highest, it means the slope of the line between their peaks is above the line for each j
between i
and k
and above or at the line for each j
after k
.
For[i = 1, i <= nm - 1, i++,
k = highest[[i]];
For[j = i + 1, j <= nm, j++,
If[j == k, Continue[]];
Build constraint, (hk - hi) * (j - i) GT/GE (hj - hi) * (k - i)
. This part seems especially clumsy. I ended up resorting to strings, but there must be a way to do it at the expression level.
yk = "h" <> ToString[k];
yi = "h" <> ToString[i];
yj = "h" <> ToString[j];
lhs = ToExpression["(" <> yk <> " - " <> yi <> ") * " <> ToString[j - i]];
rhs = ToExpression["(" <> yj <> " - " <> yi <> ") * " <> ToString[k - i]];
If[i < j, AppendTo[constraints, Greater[lhs, rhs]],
AppendTo[constraints, GreaterEqual[lhs, rhs]]];
];
];
The real work:
answer = FindInstance[constraints, vars, Integers];
Write the output as Case #N: h1 h2 ...
.
WriteString[out, "Case #", cn, ": "];
If[Length[answer] > 0,
answer = answer[[1]][[ All, 2]];
For[i = 1, i <= nm , i++, WriteString[out, answer[[i]]];
If[i < nm, WriteString[out, " "], WriteString[out, "\n"]]],
WriteString[out, "Impossible\n"]];
;
];
The "main" program. Set up in and out and cycle through the cases. I'm guessing the entire program should go in a Module
with local-only variables.
in = OpenRead["~/Documents/math/C.in"];
If[debug, out = OutputStream["stdout", 1],
out = OpenWrite["~/Documents/math/C.out"]];
ncases = Read[in, Number];
For[ic = 1, ic <= ncases, ic++, solveCase[in, out, ic]];
Close[in];
If[! debug, Close[out]];
This method takes forever for the 1000+ mountain case, so if there is a better way than FindInstance
, I'd like to hear about that, too. Or maybe the code needs to take into account higher level constraints rather than solving the most general form of the problem.
Note: After seeing no use of Mathematica (Code Jam Language Stats) in previous contests, I realized Mathematica is not allowed because there is no free version, so this is even more of an academic exercise than I intended.