作者RockLee (Now of all times)
看板Prob_Solve
标题[问题] Google Code Jam 2009, Round 2 - D
时间Wed Apr 25 19:35:13 2012
题目叙述网址:
http://code.google.com/codejam/contest/204113/dashboard#s=p3&a=3
官方解答说明:
http://code.google.com/codejam/contest/204113/dashboard#s=a&a=3
看了当初参赛者上传的 code 才知道 large dataset 怎麽解.
想要请教版上高手的是, 当遇到需要用到浮点数的运算时,
要如何决定合理的 epsilon 值呢?
以这题为例, 在判断某一个 midR 是否为一个足够大的半径时,
可能的判断方式为:
for (int i = 0; i < candidate.size() && !valid; i++)
for (int j = i; j < candidate.size() && !valid; j++)
{
Point c1 = candidate.get(i);
Point c2 = candidate.get(j);
valid = true;
for (Circle p : plant)
{
double epsilon = 1e-10;
if (midR + epsilon < p.radius) {valid = false; break;}
if (c1.distanceSquare(p.center) > Math.pow((midR + epsilon - p.radius), 2))
if (c2.distanceSquare(p.center) > Math.pow((midR + epsilon - p.radius), 2))
{valid = false; break;}
}
}
以实验的结果来说, epsilon 的值设为 1e-4 ~ 1e-13 跑出来的结果都正确,
但设为多少比较合理呢?
[小弟练习的 implementation in Java]
import java.io.*;
import java.util.*;
class D
{
private static BufferedReader input;
private static BufferedWriter output;
public static void main(String[] args)
{
try
{
input = new BufferedReader(new FileReader(args[0] + ".in"));
output = new BufferedWriter(new FileWriter(args[0] + ".out"));
String line = input.readLine();
int testcases = getInt(line, 0);
for (int testcase = 1; testcase <= testcases; testcase++)
{
line = input.readLine();
int N = getInt(line, 0);
Circle[] plant = new Circle[N];
for (int i = 0; i < N; i++)
{
line = input.readLine();
plant[i] = new Circle(getInt(line, 0), getInt(line, 1), getInt(line, 2));
}
double minR = 0, maxR = 10000;
while ((maxR - minR) > 1e-6)
{
double midR = (maxR + minR) / 2;
boolean valid = false;
ArrayList<Point> candidate = new ArrayList<Point>();
for (Circle c : plant) {candidate.add(c.center);}
for (int i = 0; i < N; i++)
for (int j = i + 1; j < N; j++)
{
Circle c1 = new Circle(plant[i].center, midR - plant[i].radius);
Circle c2 = new Circle(plant[j].center, midR - plant[j].radius);
for (Point p : c1.intersectPoints(c2)) {candidate.add(p);}
}
for (int i = 0; i < candidate.size() && !valid; i++)
for (int j = i; j < candidate.size() && !valid; j++)
{
Point c1 = candidate.get(i);
Point c2 = candidate.get(j);
valid = true;
for (Circle p : plant)
{
double epsilon = 1e-10;
if (midR + epsilon < p.radius) {valid = false; break;}
if (c1.distanceSquare(p.center) > Math.pow((midR + epsilon -
p.radius), 2))
if (c2.distanceSquare(p.center) > Math.pow((midR + epsilon -
p.radius), 2))
{valid = false; break;}
}
}
if (valid) {maxR = midR;}
else {minR = midR;}
}
String result = "Case #" + testcase + ": " + minR;
output(result);
}
input.close();
output.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static int getInt(String line, int index)
{return Integer.parseInt(getString(line, index));}
public static String getString(String line, int index)
{
line = line.trim();
while (index > 0) {line = line.substring(line.indexOf(' ') + 1); index--;}
if ((-1) == line.indexOf(' ')) {return line;}
else {return line.substring(0, line.indexOf(' '));}
}
public static void output(String s) throws Exception
{
System.out.println(s);
output.write(s);
output.newLine();
}
}
class Point
{
public final double x, y;
public Point(double x, double y) {this.x = x; this.y = y;}
public double distanceSquare(Point another)
{return (Math.pow((this.x - another.x), 2) + Math.pow((this.y - another.y),
2));}
public double distance(Point another)
{return Math.hypot((this.x - another.x), (this.y - another.y));}
}
class Circle
{
public final Point center;
public final double radius;
public Circle(Point c, double r) {center = c; radius = r;}
public Circle(double x, double y, double r) {center = new Point(x, y);
radius = r;}
public Point[] intersectPoints(Circle another)
{
Point c0 = this.center;
Point c1 = another.center;
double r0 = this.radius;
double r1 = another.radius;
double d = c0.distance(c1);
if (c0.x == c1.x && c0.y == c1.y) {return new Point[0];}
if (d > r0 + r1) {return new Point[0];}
if ((d + r0) < r1) {return new Point[0];}
if ((d + r1) < r0) {return new Point[0];}
//
http://paulbourke.net/geometry/2circle/
double a = (r0 * r0 - r1 * r1 + d * d) / (2 * d);
double h = Math.sqrt(r0 * r0 - a * a);
double x2 = c0.x + a * (c1.x - c0.x) / d;
double y2 = c0.y + a * (c1.y - c0.y) / d;
Point[] intersects = new Point[2];
intersects[0] = new Point(x2 + h * (c1.y - c0.y) / d, y2 - h * (c1.x -
c0.x) / d);
intersects[1] = new Point(x2 - h * (c1.y - c0.y) / d, y2 + h * (c1.x -
c0.x) / d);
return intersects;
}
}
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 59.126.30.95
1F:推 DJWS:比大小的时候通常不需要eps 因为误差加误差就是更大的误差 05/04 22:07
2F:→ DJWS:判断等於的时候可以设eps 因为一定有误差导致等於变成不等於 05/04 22:08
3F:→ DJWS:eps大小则是根据浮点数运算误差决定 double可以存个15位数 05/04 22:11
4F:→ DJWS:对於一般的问题来说足够精准 eps意思意思设个数字就可以了 05/04 22:14
5F:→ RockLee:感谢 D 大的经验分享~ 05/06 17:16
6F:→ RockLee:顺便分享一下我在「培养与锻链程式设计的逻辑脑」中 05/06 17:16
7F:→ RockLee:看到的作者的经验分享:(以下摘录自该书 P260) 05/06 17:17
8F:→ RockLee:通常在比较包含误差的浮动小数点值时, 05/06 17:17
9F:→ RockLee:都必须使用适当小的数 EPS,写成下面这样 05/06 17:17
10F:→ RockLee:a < b → a + EPS < b 05/06 17:18
11F:→ RockLee:a <= b → a < b + EPS 05/06 17:18
12F:→ RockLee:a == b → abs(a - b) < EPS 05/06 17:18