A - Railway Company Editorial
by
nikaj
Solution of Railway Station
Railway Station Solution by nikaj
It’s clear that it doesn’t make sense to build a station if it’s not connected to another with rails (except for the very first one, of course).
Therefore, stations form connected components. It’s also clear that these components should be trees.
The problem is quite difficult, we need to simplify lots of things to make it feasible.
Always maintain single connected component (not a forest). This may not always be optimal but counterexamples should be rare. This means that the whole process is just about building new stations and connecting them to existing.
There are multiple ways to connect. The trivial case is when station is built on the rail and we don’t need to do anything. Otherwise, we will make another simplification and pick the closest one (manhattan distance) and try to connect to it. There is another possibility, though. We could alter existing rail paths connecting two other stations, changing it slightly without affecting anything else. If the altered path passes through our new station, we don’t have to build any rails. Checking this possibility isn’t difficult: pick two orthogonal directions, find first non-empty squares in those directions (starting from target square) and if they are connected by rails, their path can be altered.
In summary, it’s a 3 step process:
Check if it’s on rail (0 rails needed)
Check if it’s on possible modified path (0 rails needed)
Check if it can be connected to closest station (distance-1 rails needed)
Now we get to another point: how to connect one station to another with rails. We will simplify again and only consider the shortest paths. We can just run DFS (much faster than BFS) on empty squares in one or two desired directions. But what if some paths block every access? We could try altering those paths.
C..
┌─A
B..
C wants to connect to B, but rails from A to B block it. We can perform path augmentation process (similar to Max Flow algorithm) and steal part of the path and delegate finding remaining path to another station.
C..
|─A
B..
Now A is again looking for shortest path to B and succeeds.
C..
|┌A
B┘.
This delegation can happen up to 3 times (see below for new station E) but in practice it doesn’t need more than 1.
─┘.B
E┌A┘
.C└D
In summary, connecting works like this:
Looks for empty path with DFS
Find augmenting path instead with modified DFS
How to build whole tree? Naturally, one by one adding new stations and for that you could try Beam Search. The evaluation function should probably include current time, money, daily income, the simplest being just idle score. Some commuter will have one connection station, which is much better than none and that could also be part of it. In the end I built simple NN (2 hidden layers up to 7 nodes each) based on these features and trained on final solutions.
But for harder cases (large M) beam search isn’t doing well.
We follow up with SA on the best found solution. Transitions change the station list:
Remove station from the list
Swap two neighbours
Insert new station
Both in beam search and SA when looking for new station we consider those that include commuters that already have one connection. If there are no such commuters, we consider all of them.
There is one more thing. Just knowing the order of stations (and rails, since they are determined based on stations) isn’t enough, we should also decide to build rails before building previous station to save time in the long run. The optimal building order can be found by dynamic programming, with following state: [time][stations][rails] -> money. Transitions are (each increase time by 1, money changes accordingly):
Wait
Build station (stations increases by 1, rails decreases by the corresponding number used by this station)
Build rail (rails increases by 1)
Some of the operations needed in the solution can be sped up (using bitmasks in some cases), but solution is still overall quite slow and far from optimal (more than 2% away from local best and probably more.
The main improvement to this solution would come from making SA faster, representing the state as a tree instead of list of stations. This would have significant impact on testcases with large M.
posted:
last update: