User:Jeff/ACM/Simplified GSM Network

Back to ACM page

Simplified GSM Network
Mobile phones have changed our lifestyle dramatically in the last decade. Mobile phones have a variety of protocols to connect with one another. One of the most popular networks for mobile phones is the GSM (Global System for Mobile Communication) network.

In a typical GSM network, a mobile phone connects with the nearest BTS (Base Transceiver Station). A BSC (Base Station Center) controls several BTSs. Several BSCs are controlled by one MSC (Mobile Services Switching Center), and this MSC maintains a connection with several other MSCs, a PSTN (Public Switched Telecom Network) and an ISDN (Integrated Services Digital Network).

This problem uses a simplified model of the conventional GSM network. Our simplified network is composed of up to fift BTS towers. When in use, a mobile phone always connects to its nearest BTS tower. The area covered by a single BTS tower is called a cell. When an active mobile phone is in motion, as it crosses cell boundaries it must seamlessly switch from one BTS to another. Given the description of a map consisting of cities, roads and BTS towers, you must determine the minimum number of BTS switches required to go from one city to another.

Each tower and each city location is to be considered as a single point in a two-dimensional Cartesian coordinate system. If there is a road between two cities, assume that the road is a straight line segment connecting these two cities. For example, in the figure, traveling on the road from city 1 to city 2 will cross two cell boundaries and thus requires two switches. Traveling from city 2 to city 5 crosses one cell boundary and traveling from city 5 to city 6 requires no switch. Traveling this route from city 1 to city 6 requires three total switches. Note that any other path from city 1 to city 6 requires more than three switches. If there is more than one possible way to get from one city to another, your program must find the optimal route.

Input
The input file contains several test cases. The first line of each test case contains four integers: B (1 &le; B &le; 50), the number of BTS towers; C (1 &le; C &le; 50), the number of cities; R (0 &le; R &le; 250), the number of roads; and Q (1 &le; Q &le; 10), the number of queries. Each of the next B lines contains two floating-point numbers x and y, the Cartesian coordinates of a BTS tower. Each of the next C lines contains two floating-point numbers xi, yi that indicate the Cartesian coordinates of the ith city (1 &le; i &le; C). Each of the next R lines contains two integers m and n (1 &le; m,n &le; C), which indicate the there is a road between the mth and the nth city. Each of the next Q lines contains two integers s and d (1 &le; s,d &le; C), the source and destination cities.

No coordinate will have an absolute value greater than 1000. No two towers will be at the same location. No two cities will be at the same location, and no city will lie on a cell boundary. No road will be coincident with a cell boundary nor contain a point lying on the boundary of three or more cells.

The input will end with a line containing four zeros.

Output
For each input set, you should produce Q+1 lines of output, as shown below. The first line should contain the number of the test case. Q lines should follow, one for each query, each containing an integer indicating the minimum number of switches required to go from city s to city d. If it is not possible to go from city s to city d, print the line  instead.

Analysis
There are two aspects to this algorithm. For each road in the map, we first need to calculate how many cell boundaries it crosses. We then use this information to solve a series of point-to-point shortest path problems.

Finding the cost of a road
This is a graph theory problem, where the cities are nodes in the graph and the roads are edges. The weight, or cost, of each road is equal to the number of cell boundaries it intersects. We could find this number using a lot of advanced geometry, or we could use a simple recursive approach. All we need are a couple of helper functions: one to find the squared distance between two points, and one to find the closest tower to a given point.

typedef pair p2d; double dist2(p2d &a, p2d &b) { return (a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y); } int closestTower(vector &towers, vector &p) { double minDist(INF), ans(0); for (int i(0); i < towers.size; ++i) { if (minDist < dist2(towers[i], p)) continue; minDist = dist2(towers[i], p); ans = i; } return ans; }
 * 1) define X first
 * 2) define Y second

To program the recursive function, there are three cases to consider. First, if the two cities share the same closest tower, then they are inside the same cell; no boundaries will be crossed while driving from cityA to cityB. Otherwise, if the two cities are so close to each other that they are practically the same point, but they still have two different closest providers, then there must be exactly one cell boundary separating them. The third case, when the cities are a normal distance apart but have different closest towers, is the recursive case. Find the midpoint of the line segment connecting the two cities, and sum the distances from the midpoint to each city.

int roadCost(vector &towers, p2d &cityA, p2d &cityB) { if (closestTower(towers, cityA) == closestTower(towers, cityB)) return 0; // the two cities are inside the same cell boundary if (dist2(cityA, cityB) < EPSILON) return 1; // one cell boundary separates the cities p2d mid((cityA.X + cityB.X) / 2, (cityA.Y + cityB.Y) / 2); return roadCost(towers, cityA, mid) + roadCost(towers, mid, cityB); }
 * 1) define EPSILON 1e-9

Dijkstra's algorithm
Once we have calculated a cost for each road, the problem is reduced to finding the shortest path in an undirected, weighted graph. The best way to do this is with a modified Dijkstra's algorithm. We'll wrap the algorithm in a class, and use a separate instantiation for each query in the input.

typedef pair p2i; struct Dijkstra { map cost; set queue; int city; Dijkstra(int start) { cost[start] = 0; queue.insert(p2i(0, start)); }  p2i pop { city = queue.begin->Y; queue.erase(queue.begin); return city; }  int distance { return cost[city]; }  void push(int dest, int weight) { weight += distance; if (cost.find(dest) == cost.end) { cost[dest] = weight; queue.insert(p2i(weight, dest)); return; }    if (cost[dest] <= weight) return; queue.erase(p2i(cost[dest], dest)); cost[dest] = weight; queue.insert(p2i(weight, dest)); } };