5 * Solution for NEERC'2009 Problem A: Asteroids
6 * This solution checks correctness of the input.
7 * @author Roman Elizarov
9 public class asteroids_re
{
10 static final boolean DEBUG
= false;
12 public static void main(String
[] args
) throws Exception
{
13 new asteroids_re().go();
16 void go() throws Exception
{
32 assert Math
.abs(x
) <= 10000;
33 assert Math
.abs(y
) <= 10000;
34 assert Math
.abs(z
) <= 10000;
38 public String
toString() {
39 return "(" + x
+ "," + y
+ "," + z
+ ")";
43 return x
== 0 && y
== 0 && z
== 0;
47 return Math
.sqrt(x
* x
+ y
* y
+ z
* z
);
50 V3D(double x
, double y
, double z
) {
57 return new V3D(x
+ o
.x
, y
+ o
.y
, z
+ o
.z
);
61 return new V3D(x
- o
.x
, y
- o
.y
, z
- o
.z
);
72 return x
* v
.x
+ y
* v
.y
+ z
* v
.z
;
76 return new V3D(x
* f
, y
* f
, z
* f
);
80 return new V3D(x
/ f
, y
/ f
, z
/ f
);
89 final List
<V3D
> verts
= new ArrayList
<V3D
>();
92 public String
toString() {
93 return n
.toString() + "," + d
;
100 double angleBetween(V3D u
, V3D v
) {
102 double sin
= w
.dot(v
) / w
.length() / v
.length();
103 double cos
= u
.dot(v
) / u
.length() / v
.length();
104 return Math
.atan2(sin
, cos
);
107 public double dist(V3D v
) {
108 return Math
.abs(side(v
) / n
.length());
112 static class Polyhedra
{
115 final List
<Set
<Face
>> vfaces
= new ArrayList
<Set
<Face
>>();
116 final List
<Face
> faces
= new ArrayList
<Face
>();
118 Polyhedra(Scanner in
) {
121 assert n
>= 4 && n
<= 60;
123 for (int i
= 0; i
< n
; i
++) {
124 verts
[i
] = new V3D(in
);
129 for (int i
= 0; i
< n
; i
++) {
130 vfaces
.add(new HashSet
<Face
>());
132 for (int i
= 0; i
< n
; i
++) {
133 for (int j
= i
+ 1; j
< n
; j
++) {
134 for (int k
= j
+ 1; k
< n
; k
++) {
140 List
<Integer
> flen
= new ArrayList
<Integer
>();
141 for (Face face
: faces
) {
142 flen
.add(face
.verts
.size());
144 Collections
.sort(flen
, Collections
.reverseOrder());
145 System
.out
.println("FACES: " + flen
);
147 assert faces
.size() > 1; // non-degenerate!
148 for (Set
<Face
> vface
: vfaces
) {
149 assert !vface
.isEmpty(); // convex! (each point on a face)
153 void checkFace(int i
, int j
, int k
) {
154 if (!intersect(vfaces
.get(i
), vfaces
.get(j
), vfaces
.get(k
)).isEmpty())
155 return; // already on a common face
157 V3D u
= verts
[j
].sub(w
);
158 V3D v
= verts
[k
].sub(w
);
159 final Face f
= new Face();
161 assert !f
.n
.isZero(); // no 3 points on a line
164 List
<Integer
> indices
= new ArrayList
<Integer
>();
165 for (int t
= 0; t
< n
; t
++) {
167 double tside
= f
.side(tv
);
170 return; // not a face
172 } else if (tside
> 0) {
174 return; // not a face
182 // make sure that "inside" has a positive value of side
187 for (int t
: indices
) {
188 vfaces
.get(t
).add(f
);
190 // sort verices on a face by angle
191 final V3D center
= avg(f
.verts
);
192 final V3D v0
= w
.sub(center
);
193 assert Math
.abs(f
.side(center
)) < 1; // just sanity check that center is on a plane
194 Collections
.sort(f
.verts
, new Comparator
<V3D
>() {
195 public int compare(V3D o1
, V3D o2
) {
196 return Double
.compare(f
.angleBetween(v0
, o1
.sub(center
)), f
.angleBetween(v0
, o2
.sub(center
)));
202 V3D v0
= avg(Arrays
.asList(verts
));
203 V3D vsum
= new V3D(0, 0, 0);
205 for (Face face
: faces
) {
206 V3D v1
= face
.verts
.get(0);
207 for (int i
= 2; i
< face
.verts
.size(); i
++) {
208 V3D v2
= face
.verts
.get(i
- 1);
209 V3D v3
= face
.verts
.get(i
);
210 double v
= Math
.abs(v1
.sub(v0
).vec(v2
.sub(v0
)).dot(v3
.sub(v0
)) / 6);
211 vsum
= vsum
.add(avg(Arrays
.asList(v0
, v1
, v2
, v3
)).mul(v
));
215 V3D center
= vsum
.div(volume
); // center of mass
216 double min
= Double
.MAX_VALUE
;
217 for (Face face
: faces
) {
218 min
= Math
.min(min
, face
.dist(center
));
221 System
.out
.println("Center: " + center
+ "; Volume: " + volume
+ "; Min:" + min
);
226 boolean containsAny(V3D
[] verts
) {
227 for (V3D vert
: verts
) {
234 boolean contains(V3D v
) {
235 for (Face face
: faces
) {
236 if (face
.side(v
) < -1e-6)
237 return false; // definitely outside
243 static V3D
avg(List
<V3D
> verts
) {
244 V3D sum
= new V3D(0, 0, 0);
245 for (V3D v
: verts
) {
248 return sum
.div(verts
.size());
251 static <T
> Set
<T
> intersect(Set
<T
> a
, Set
<T
> b
, Set
<T
> c
) {
252 Set
<T
> result
= new HashSet
<T
>(a
);
261 void read() throws Exception
{
262 Scanner in
= new Scanner(new File("asteroids.in"));
263 in
.useLocale(Locale
.US
);
264 p
= new Polyhedra(in
);
265 q
= new Polyhedra(in
);
274 assert !p
.containsAny(q
.verts
);
275 assert !q
.containsAny(p
.verts
);
276 result
= p
.solve() + q
.solve();
279 void write() throws Exception
{
280 PrintWriter out
= new PrintWriter("asteroids.out");
285 //----------------- just for validation ------------------
288 * Strict scanner to veryfy 100% correspondence between input files and input file format specification.
289 * It is a drop-in replacement for {@link java.util.Scanner} that could be added to a soulution source
290 * (cut-and-paste) without breaking its ability to work with {@link java.util.Scanner}.
292 public class Scanner
{
293 private final BufferedReader in
;
294 private String line
= "";
297 private boolean localeset
;
299 public Scanner(File source
) throws FileNotFoundException
{
300 in
= new BufferedReader(new FileReader(source
));
304 public void close() {
305 assert line
== null : "Extra data at the end of file";
308 } catch (IOException e
) {
309 throw new AssertionError("Failed to close with " + e
);
313 public void nextLine() {
314 assert line
!= null : "EOF";
315 assert pos
== line
.length() : "Extra characters on line " + lineNo
;
317 line
= in
.readLine();
318 } catch (IOException e
) {
319 throw new AssertionError("Failed to read line with " + e
);
325 public String
next() {
326 assert line
!= null : "EOF";
327 assert line
.length() > 0 : "Empty line " + lineNo
;
329 assert line
.charAt(0) > ' ' : "Line " + lineNo
+ " starts with whitespace";
331 assert pos
< line
.length() : "Line " + lineNo
+ " is over";
332 assert line
.charAt(pos
) == ' ' : "Wrong whitespace on line " + lineNo
;
334 assert pos
< line
.length() : "Line " + lineNo
+ " is over";
335 assert line
.charAt(0) > ' ' : "Line " + lineNo
+ " has double whitespace";
337 StringBuilder sb
= new StringBuilder();
338 while (pos
< line
.length() && line
.charAt(pos
) > ' ')
339 sb
.append(line
.charAt(pos
++));
340 return sb
.toString();
343 public int nextInt() {
345 assert s
.length() == 1 || s
.charAt(0) != '0' : "Extra leading zero in number " + s
+ " on line " + lineNo
;
346 assert s
.charAt(0) != '+' : "Extra leading '+' in number " + s
+ " on line " + lineNo
;
348 return Integer
.parseInt(s
);
349 } catch (NumberFormatException e
) {
350 throw new AssertionError("Malformed number " + s
+ " on line " + lineNo
);
354 public double nextDouble() {
355 assert localeset
: "Locale must be set with useLocale(Locale.US)";
357 assert s
.length() == 1 || s
.startsWith("0.") || s
.charAt(0) != '0' : "Extra leading zero in number " + s
+ " on line " + lineNo
;
358 assert s
.charAt(0) != '+' : "Extra leading '+' in number " + s
+ " on line " + lineNo
;
360 return Double
.parseDouble(s
);
361 } catch (NumberFormatException e
) {
362 throw new AssertionError("Malformed number " + s
+ " on line " + lineNo
);
366 public void useLocale(Locale locale
) {
367 assert locale
== Locale
.US
;