Last active
September 14, 2015 18:53
-
-
Save dragon0/39e5b8a06cd33b147806 to your computer and use it in GitHub Desktop.
ACM Airports problem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.HashMap; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Scanner; | |
import java.util.concurrent.Callable; | |
import java.util.concurrent.CompletionService; | |
import java.util.concurrent.ExecutionException; | |
import java.util.concurrent.ExecutorCompletionService; | |
import java.util.concurrent.ExecutorService; | |
import java.util.concurrent.Executors; | |
import java.util.concurrent.Future; | |
public class ACMAirport { | |
// Earth's radius | |
public static final double R = 3958.75; | |
/** | |
* Represents the Latitude and Longitude of a point on Earth's surface, | |
* in radians. | |
* | |
*/ | |
private static class Coordinate{ | |
private final double lat; | |
private final double lon; | |
public Coordinate(int latDeg, int latMin, int latSec, String ns, int lonDeg, int lonMin, int lonSec, String ew){ | |
// latitude is given in degrees, minutes, seconds; convert to decimal | |
double lat = latDeg; | |
lat += latMin / 60.0; | |
lat += latSec / (60.0 * 60.0); | |
// adjust sign | |
switch(ns){ | |
case "n": | |
case "N": | |
break; | |
case "s": | |
case "S": | |
lat = -lat; | |
break; | |
default: | |
throw new IllegalArgumentException("ns must be n or s, got " + ns); | |
} | |
// store | |
this.lat = Math.toRadians(lat); | |
// likewise | |
double lon = lonDeg; | |
lon += lonMin / 60.0; | |
lon += lonSec / (60.0 * 60.0); | |
switch(ew){ | |
case "e": | |
case "E": | |
break; | |
case "w": | |
case "W": | |
lon = -lon; | |
break; | |
default: | |
throw new IllegalArgumentException("ew must be e or w, got " + ew); | |
} | |
this.lon = Math.toRadians(lon); | |
} | |
public double getLon() { | |
return lon; | |
} | |
public double getLat() { | |
return lat; | |
} | |
} | |
// map from airport code to airport's location | |
private static final Map<String, Coordinate> airportLocations = new HashMap<>(); | |
/** | |
* Insert an airport into airportLocations. | |
* | |
* line contains: | |
* the airport code; | |
* degrees, minutes, seconds latitude; | |
* and degrees, minutes, seconds longitude. | |
* eg "MAD 40 28 20 N 03 33 39 W" | |
* | |
* @param line A String containing the airport code | |
*/ | |
private static void insert(String line){ | |
// Using a regex would probably allow for better error checking | |
String[] tokens = line.split(" "); | |
String code = tokens[0]; | |
int latDeg = Integer.parseInt( tokens[1]); | |
int latMin = Integer.parseInt( tokens[2]); | |
int latSec = Integer.parseInt( tokens[3]); | |
String ns = tokens[4]; | |
int lonDeg = Integer.parseInt( tokens[5]); | |
int lonMin = Integer.parseInt( tokens[6]); | |
int lonSec = Integer.parseInt( tokens[7]); | |
String ew = tokens[8]; | |
Coordinate c = new Coordinate(latDeg, latMin, latSec, ns, lonDeg, lonMin, lonSec, ew); | |
airportLocations.put(code, c); | |
} | |
/** | |
* Read test data from System.in | |
* @param args | |
*/ | |
public static void main(String[] args) { | |
String data = | |
"MAD 40 28 20 N 03 33 39 W\n" | |
+"CMN 33 22 04 N 07 35 16 W\n" | |
+"RAK 31 36 24 N 08 02 10 W\n" | |
+"HNL 21 18 57 N 157 55 36 W\n" | |
+"YYZ 43 40 38 N 79 37 50 W\n" | |
+"FRA 50 01 35 N 08 32 35 E\n" | |
+"LAS 36 04 49 N 115 09 08 W\n" | |
+"LAX 33 56 33 N 118 24 29 W\n" | |
+"---\n" | |
+"LAS LAX HNL LAX LAS\n" | |
+"LAX YYZ FRA CMN\n" | |
+"LAX MAD RAK\n" | |
; | |
/* | |
MAD 40 28 20 N 03 33 39 W | |
CMN 33 22 04 N 07 35 16 W | |
RAK 31 36 24 N 08 02 10 W | |
HNL 21 18 57 N 157 55 36 W | |
YYZ 43 40 38 N 79 37 50 W | |
FRA 50 01 35 N 08 32 35 E | |
LAS 36 04 49 N 115 09 08 W | |
LAX 33 56 33 N 118 24 29 W | |
--- | |
LAS LAX HNL LAX LAS | |
LAX YYZ FRA CMN | |
LAX MAD RAK | |
*/ | |
Scanner in = new Scanner(System.in); | |
//Scanner in = new Scanner(data); | |
String line; | |
// create airport location table | |
while(in.hasNextLine() && !(line = in.nextLine()).equals("---")){ | |
insert(line.trim()); | |
} | |
// build a thread pool to compute distances concurrently | |
// C++ can omit this | |
int cores = Runtime.getRuntime().availableProcessors(); | |
ExecutorService pool = Executors.newFixedThreadPool(cores); | |
CompletionService<Double> cs = new ExecutorCompletionService<>(pool); | |
List<Future<Double>> results = new LinkedList<>(); | |
// read paths and begin computing distances | |
while(in.hasNextLine()){ | |
DistanceTask task = new DistanceTask(in.nextLine().trim()); | |
// executes task.call() in tread pool | |
// C++ should just do cout << task.call() << endl; | |
results.add(cs.submit(task)); | |
} | |
// results are read in the same order they are submitted | |
for(Future<Double> result : results){ | |
try { | |
System.out.println(Math.round(result.get())); | |
} catch (InterruptedException | ExecutionException e) { | |
e.printStackTrace(); | |
} | |
} | |
pool.shutdown(); | |
in.close(); | |
} | |
private static class DistanceTask implements Callable<Double>{ | |
// space-delimited list of airport codes | |
private final String line; | |
public DistanceTask(String line) { | |
this.line = line; | |
} | |
/** | |
* Computes the total distance of a list of airport connections | |
* | |
*/ | |
@Override | |
public Double call() throws Exception { | |
String[] airports = line.split(" "); | |
double distance = 0.0; | |
Coordinate prevCoord = null; | |
Coordinate curCoord = airportLocations.get(airports[0]); | |
for(int i = 1; i < airports.length; i++){ | |
prevCoord = curCoord; | |
curCoord = airportLocations.get(airports[i]); | |
distance += greatCircleDistance(prevCoord, curCoord); | |
} | |
return distance; | |
} | |
/** | |
* Calculate the Great-Circle distance between from and to. | |
* https://en.wikipedia.org/wiki/Great-circle_distance | |
* | |
* @param from latitude and longitude of departure | |
* @param to latitude and longitude of destination | |
* @return the great-circle distance | |
*/ | |
private double greatCircleDistance(Coordinate from, Coordinate to) { | |
double deltalon = to.getLon() - from.getLon(); | |
double theta = Math.acos( | |
Math.sin(from.getLat()) * Math.sin(to.getLat()) | |
+ Math.cos(from.getLat()) * Math.cos(to.getLat()) * Math.cos(deltalon)); | |
return R * theta; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=669&page=show_problem&problem=5147