World Final 1998 Crystal Clear
Posted in World Final on 十二月 2nd, 2010 by 纳米 – Be the first to comment好久没更新了,继续更新吧。不过这次也该改目录名了,我也不是OIer了,要转型ACMer了。
题目大意:
以坐标系上每个整数坐标的点为圆心,直径为1建立圆。给定一个多边形,多边形上每条边的端点坐标都是整数。求被覆盖的圆的有效面积。
有效面积是这样定义的:
1. 如果整个圆都在多边形内,那么它的面积都是有效的;
2. 如果多边形的边穿过圆心,那么在多边形内的扇形面积是有效的。
解题方法:
因为坐标范围都很小,并且多边形的顶点都是整数,所以可以对于可能有效的圆,分别计算。对于每一个可能有效的圆:
1. 判断圆心是不是跟多边形某个顶点重合。如果是,累加扇形面积,结束;否则,转到2。
2. 判断圆心是否在某条边上。如果是,累加半圆面积,结束;否则,转到3.
3. 判断圆是否在多边形内。如果是,累加圆面积,结束。
圆在多边形内,当且仅当圆心离每一条边的距离不小于0.5,且圆心在多边形内。
几何知识(感谢gXX同学):
以下用*表示叉积,@表示点积。
1. 叉积、点积、欧几里得距离。
2. 计算两向量夹角角度:利用余弦定理。
3. 点在线段上,点为p,线段端点p1、p2,v1、v2对应p到p1、p2的向量,下同:当且仅当v1 * v2 = 0,且v1 @ v2 < 0
4. 点到线段所在直线距离:d = |v1 * v2| / Distance(p1, p2)
5. 判断点在多边形内:射线法和转角法,参见《算法艺术与信息学竞赛》P381。代码用转角法实现。
注意事项:
用余弦定理和反三角函数只能算出锐角,但是扇形可能对应一个钝角。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | #include <cstdio> #include <cmath> const double pi = 3.1415926535897932384626433832795; const int maxn = 27; int n; struct POINT { int x, y; POINT() { x = y = 0; } bool operator != (const POINT &value) { const POINT *that = &value; return this->x != that->x || this->y != that->y; } POINT operator -(const POINT &value) { const POINT *that = &value; POINT result; result.x = this->x - that->x; result.y = this->y - that->y; return result; } } px[maxn]; bool input() { scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%d%d", &px[i].x, &px[i].y); return (bool) n; } inline int min(int x, int y) { return x < y ? x : y; } inline int max(int x, int y) { return x > y ? x : y; } inline int CrossProduct(POINT v1, POINT v2) { return v1.x * v2.y - v2.x * v1.y; } inline int DotProduct(POINT v1, POINT v2) { return v1.x * v2.x + v1.y * v2.y; } inline double Distance(POINT p1, POINT p2) { return sqrt((double) ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y))); } inline double Angle(POINT p0, POINT p1, POINT p2) { POINT v0, v1, v2; v1 = p1 - p0; v2 = p2 - p0; return acos(DotProduct(v1, v2) / (Distance(v0, v1) * Distance(v0, v2))); } inline bool PointOnLine(POINT p, POINT p1, POINT p2) { POINT v1, v2; v1 = p1 - p; v2 = p2 - p; return !CrossProduct(v1, v2) && DotProduct(v1, v2) < 0; } inline double PointToLineDistance(POINT p, POINT p1, POINT p2) { POINT v1, v2; v1 = p1 - p; v2 = p2 - p; return fabs(CrossProduct(v1, v2) / Distance(p1, p2)); } inline bool PointInPolygon(POINT p, POINT px[], int n) { // px[0] = px[n]; double AngleSum = 0; for (int i = 1; i <= n; ++i) { int cp = CrossProduct(px[i - 1] - p, px[i] - p); if (cp > 0) { AngleSum += Angle(p, px[i - 1], px[i]); } else if (cp < 0) { AngleSum -= Angle(p, px[i - 1], px[i]); } } return fabs(AngleSum) > pi; } double solve() { double result = 0; POINT minp = px[1], maxp = px[1], p; for (int i = 2; i <= n; ++i) { minp.x = min(minp.x, px[i].x); minp.y = min(minp.y, px[i].y); maxp.x = max(maxp.x, px[i].x); maxp.y = max(maxp.y, px[i].y); } px[0] = px[n]; px[n + 1] = px[1]; for (p.x = minp.x; p.x <= maxp.x; ++p.x) for (p.y = minp.y; p.y <= maxp.y; ++p.y) { bool processed = false; if (!processed) { int k = 1; while (k <= n && px[k] != p) ++k; if (k <= n) { if (CrossProduct(px[k] - px[k - 1], px[k + 1] - px[k]) < 0) result += Angle(px[k], px[k - 1], px[k + 1]) / 8; else result += (2 * pi - Angle(px[k], px[k - 1], px[k + 1])) / 8; processed = true; } } if (!processed) { int k = 1; while (k <= n && !PointOnLine(p, px[k - 1], px[k])) ++k; if (k <= n) { result += pi / 8; processed = true; } } if (!processed) { bool broken = false; for (int i = 1; i <= n && !broken; ++i) broken = PointToLineDistance(p, px[i - 1], px[i]) < 0.5; if (!broken && PointInPolygon(p, px, n)) { result += pi / 4; processed = true; } } } return result; } int main() { int Cases = 0; while (input()) printf("Shape %d\ninsulating area = %.3f cm^2\n", ++Cases, solve()); return 0; } |