题意就是,一个坐标轴上,一个西边村庄在(0,0),然后有一条河在x=a和x=b夹着的地方(a < b),西边村庄有n条路到达a河岸纵坐标为y的路,都是直路。x=b的右边有一个村庄,有m条路到达河岸b,但是这些路不是直的,只知道这些路各自的长度是L。现在要你在河岸两边选一点,建一座桥,使得西边村庄到东边村庄的距离最短。输出选西边哪个点和东边哪个点来建桥。
解法:真的是想了两三个小时没有想出来怎么做好。。。然后找了个题解。大致就是,两点之间直线最短。选取b河岸上一点,他所能到达a的方式最优的只有一条路,选取a河岸上的点肯定是最靠近它和东边村庄的连线的那个点,两点之间直线最短嘛。所以我们将a河岸上的点排一下序。枚举b河岸上的点,然后不断找a河岸上离当前枚举的点和东边村庄的连线最近的点来更新答案即可。复杂度O(mlogn)。
代码如下:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
typedef pair<double, int> pii;
const int maxn = 1e5 + 5;
const double eps = 1e-10;
int n, m;
double a[maxn], b[maxn], l[maxn];
vector <pii> v;
double Count(double x1, double y1, double x2, double y2) {
return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif
int ans1, ans2;
double x1, x2;
scanf("%d%d%lf%lf", &n, &m, &x1, &x2);
for(int i = 0; i < n; i++) {
scanf("%lf", &a[i]);
v.push_back(pii(a[i], i));
}
for(int i = 0; i < m; i++) {
scanf("%lf", &b[i]);
}
for(int i = 0; i < m; i++) {
scanf("%lf", &l[i]);
}
sort(a, a + n);
sort(v.begin(), v.end());
double Min = 1e18;
for(int i = 0; i < m; i++) {
double k = b[i] / x2;
double y = k * x1;
int p = lower_bound(a, a + n, y) - a;
if(p < n) {
double tmp = Count(0, 0, x1, a[p]) + Count(x1, a[p], x2, b[i]) + l[i];
if(Min - tmp > eps) {
Min = tmp;
ans1 = v[p].second;
ans2 = i;
}
}
if(p - 1 >= 0) {
double tmp = Count(0, 0, x1, a[p - 1]) + Count(x1, a[p - 1], x2, b[i]) + l[i];
if(Min - tmp > eps) {
Min = tmp;
ans1 = v[p - 1].second;
ans2 = i;
}
}
}
// printf("%f\n", Min);
printf("%d %d\n", ans1 + 1, ans2 + 1);
return 0;
}