POJ 1375 Intervals
[★★☆☆☆]计算几何
题目大意:
在二维空间中,最上方一个点作为灯 ,下面有几个圆遮挡光线,求地面(x=0)上的阴影区间。
输入格式:
N(N个遮挡物,N=0时结束程序)
bx, by(灯的坐标)
N行:cxi, cyi, ri(遮挡物的坐标和半径)输出格式:
阴影区间(从左到右输出,保留两位小数)
样例
输入:
6
300 450
70 50 30
120 20 20
270 40 10
250 85 20
220 30 30
380 100 100
1
300 300
300 150 90
1
300 300
390 150 90
0输出:
0.72 78.86
88.50 133.94
181.04 549.9375.00 525.00
300.00 862.50
解题思路:
水题,除了推导公式的时候变量有一点多,稍微细心一点就没什么问题。
首先对每个圆设切线坐标,根据切线到圆心距离等于半径解出直线方程,得到阴影区间。在此次注意直线设成 x = ay + x0 格式,避免了斜率不存在的情况,求直线与横坐标轴也特别方便(就是x0)。
然后对得到的区间根据左边的x值进行排序。
然后遍历区间数组,区间重叠就合起来,不重叠就输出。代码
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 500;
struct qujian{
double qx, qy;
};
double lx, ly;
double cx[MAXN], cy[MAXN], r[MAXN];
//double a[MAXN][2];
qujian qj[MAXN];
double pf(double a) {
return a*a;
}
bool comp(qujian t1, qujian t2) {
return t1.qx < t2.qx;
}
//设直线 x = a * y + x0;
// x0 = lx - a*ly;
// 所以 x = a*y + lx - a*ly
int main() {
int n;
while (cin >> n) {
if (n == 0) break;
cin >> lx >> ly;
for (int i = 0; i < n; i++) {
cin >> cx[i] >> cy[i] >> r[i];
}
for (int i = 0; i < n; i++) {
double ta, tb, tc, dt;
double ans1, ans2;
ta = (pf(cy[i]) + pf(ly) - 2*cy[i]*ly - pf(r[i]));
tb = 2*(cy[i]*lx - cy[i]*cx[i] - lx*ly + ly*cx[i]);
tc = (pf(lx) + pf(cx[i]) - 2*lx*cx[i] - pf(r[i]));
dt = pf(tb) - 4*ta*tc;
ans1 = (-tb - sqrt(dt))/(2*ta);
ans2 = (-tb + sqrt(dt))/(2*ta);
// a[i][0] = ans1; a[i][1] = ans2;
qj[i].qx = lx - ans1*ly; qj[i].qy = lx - ans2*ly;
if (qj[i].qx > qj[i].qy) swap(qj[i].qx, qj[i].qy);
}
sort(qj, qj+n, comp);
// for (int i = 0; i < n; i++) {
// cout << qj[i].qx << ' ' << qj[i].qy << endl;
// }
double nl, nr; // now_left, now_right
nl = qj[0].qx; nr = qj[0].qy;
for (int i = 1; i < n; i++) {
if (qj[i].qx <= nr) {
if (qj[i].qy > nr) nr = qj[i].qy;
}
else {
printf("%.2lf %.2lf\n", nl, nr);
nl = qj[i].qx; nr = qj[i].qy;
}
}
printf("%.2lf %.2lf\n\n", nl, nr);
}
return 0;
}