[USACO 6.4.2] Electric Fences
题目大意
给出F条线段,这些线段所在的直线和X或Y轴平行.要求一点到这些线段的距离之和最短.注意: 这里的点和线段的距离是指点到线段上某一点使得它们连成线段最短的长度.
题解
我们先不要考虑这么复杂的问题先.先考虑一下简单规模的.也就是说在一条轴上的情况.
也就是说这里的线段会变成轴上的一点.如果我们要求此轴上一点使得此点到其它已给出的点距离之和最短.那么有人或许会说,很简单啊,不就是一个中位数的问题嘛.但是这样当我们扩展到二维情形时,中位数显然不适用.那么我们就得找它们之间的共通之处.
比如说,我们来找一下函数关系.我们可以找到一个距离之和S关于答案点坐标x的函数式,此函数是一个二次函数.
也就说,我们知道了当y固定时的答案.
其实,二维的情形就相当于两个二次函数相加.PALAPALAPALA.
所以,我们只是需要采取三分套三分的方法就可以AC了.第一次三分x,第二次三分y再判断一下.不断找最小值.
代码
/* TASK:fence3 LANG:C++ */ #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; int n; int line[155][4]; double ansx, ansy, ansd; double sqr(double x) { return x * x; } double calc(double x, double y) { double sum = 0; for (int i = 0; i < n; ++i) { double tmp = min(sqrt(sqr(line[i][0] - x) + sqr(line[i][1] - y)), sqrt(sqr(line[i][2] - x) + sqr(line[i][3] - y))); if (line[i][1] == line[i][3] && line[i][0] <= x && x <= line[i][2]) tmp = min(tmp, fabs(line[i][1] - y)); if (line[i][0] == line[i][2] && line[i][1] <= y && y <= line[i][3]) tmp = min(tmp, fabs(line[i][0] - x)); sum += tmp; } return sum; } double thy(double x) { double l = 0, r = 100; while (r - l >= 0.01) { double mid = (l + r) / 2; double midmid = (r + mid) / 2; double tmp1 = calc(x, mid), tmp2 = calc(x, midmid); if (tmp1 < tmp2) r = midmid; else l = mid; } double tmpd = calc(x, l); if (ansd > tmpd) { ansd = tmpd; ansx = x; ansy = l; } return tmpd; } void thx(double l, double r) { while (r - l >= 0.01) { double mid = (l + r) / 2; double midmid = (r + mid) / 2; double tmp1 = thy(mid), tmp2 = thy(midmid); if (tmp1 < tmp2) r = midmid; else l = mid; } } int main() { freopen("fence3.in", "r", stdin); freopen("fence3.out", "w", stdout); scanf("%d", &n); for (int i = 0; i < n; ++i) scanf("%d%d%d%d", &line[i][0], &line[i][1], &line[i][2], &line[i][3]); ansd = 0x7fffffff; thx(0, 100); printf("%.1lf %.1lf %.1lf\n", ansx, ansy, ansd); return 0; }