平面最近點對
阿新 • • 發佈:2021-12-23
平面最近點對
問題描述:給你\(n\)個在二維平面上的點,讓你求任意兩個點之間的歐幾里得距離的最小值
P1257 平面上的最接近點對
此題\(2 \leq n \leq 10^4\) ,所以可以暴力列舉任意兩個點求距離然後取最小值
時間複雜度:\(O(n^2)\) 可以通過此題
參考程式碼:
#include<bits/stdc++.h> using namespace std; #define ll long long const int N = 1e4 + 5; ll px[N], py[N]; int n; long long mx = 100000000000; ll dis(int i, int j) { return (px[i] - px[j]) * (px[i] - px[j]) + (py[i] - py[j]) * (py[i] - py[j]); } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for (int i = 1; i <= n; ++i) { cin >> px[i] >> py[i]; for (int j = 1; j < i; ++j) mx = min(mx, dis(i, j)); } printf("%.4f", sqrt(mx)); return 0; }
P1429 平面最近點對(加強版)
此題\(2 \leq n \leq 2e5\) ,顯然需要更優複雜度的做法-分治,此處可以去參考平面最近點對講解的分治做法,複雜度為:\(O(nlog^2n)\)
非分治做法也可參考上述連結內的相關部分
參考程式碼:
#include<bits/stdc++.h> struct Point { double x, y; void read() { scanf("%lf%lf", &x, &y); return; } Point(double _x = 0.0 , double _y = 0.0):x(_x) , y(_y){} bool operator < (const Point& a) const { return y < a.y; } }; double res; void update(const Point& a, const Point& b) { double dx = sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); res = std::min(res, dx); return; } const int N = 1e6 + 5; Point points[N], t[N]; void solve(int lr, int rs) { if (lr >= rs) return; if (rs - lr == 1) { update(points[lr], points[rs]); std::sort(points + lr, points + rs + 1); return; } int mid = lr + rs >> 1; double midx = points[mid].x; solve(lr, mid); solve(mid + 1, rs); int l = lr, r = mid + 1, k = 0; while (l <= mid && r <= rs) { if (points[l] < points[r]) t[++k] = points[l++]; else t[++k] = points[r++]; } while (l <= mid) t[++k] = points[l++]; while (r <= rs) t[++k] = points[r++]; k = 1; for (int i = lr; i <= rs; ++i) points[i] = t[k++]; int size = 0; for (int i = lr; i <= rs; ++i) { if (std::abs(points[i].x - midx) < res) { for (int j = size - 1; j >= 0 && points[i].y - t[j].y < res; --j) update(points[i], t[j]); t[size++] = points[i]; } } return; } int n; int main() { scanf("%d", &n); for (int i = 1; i <= n; ++i) points[i].read(); std::sort(points + 1, points + 1 + n, [&](const Point& a, const Point& b) { return a.x < b.x || (a.x == b.x && a.y < b.y); }); res = 1e20; solve(1, n); printf("%.4lf\n", res); return 0; }
P7883 平面最近點對(加強加強版)
此題\(2 \leq n \leq 4 \times 10 ^ 5\),但時限只有\(350ms\),所以只能使用上面的\(O(nlog^2n)\)做法,注意這題輸出的是\(D^2\),且這題距離最大值為\(2 \times 10^{14}\)在\(double\)型別精度的表示範圍內,故只需要在上述程式碼的輸出部分進行修改即可。(我使用不損失精度的記錄答案的方法\(TLE\)了)
參考程式碼:
#include<bits/stdc++.h> struct Point { double x, y; void read() { scanf("%lf%lf", &x, &y); return; } Point(double _x = 0.0, double _y = 0.0) :x(_x), y(_y) {} bool operator < (const Point& a) const { return y < a.y; } }; double res; void update(const Point& a, const Point& b) { double dx = sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); res = std::min(res, dx); return; } const int N = 1e6 + 5; Point points[N], t[N]; void solve(int lr, int rs) { if (lr >= rs) return; if (rs - lr == 1) { update(points[lr], points[rs]); std::sort(points + lr, points + rs + 1); return; } int mid = lr + rs >> 1; double midx = points[mid].x; solve(lr, mid); solve(mid + 1, rs); int l = lr, r = mid + 1, k = 0; while (l <= mid && r <= rs) { if (points[l] < points[r]) t[++k] = points[l++]; else t[++k] = points[r++]; } while (l <= mid) t[++k] = points[l++]; while (r <= rs) t[++k] = points[r++]; k = 1; for (int i = lr; i <= rs; ++i) points[i] = t[k++]; int size = 0; for (int i = lr; i <= rs; ++i) { if (std::abs(points[i].x - midx) < res) { for (int j = size - 1; j >= 0 && points[i].y - t[j].y < res; --j) update(points[i], t[j]); t[size++] = points[i]; } } return; } int n; int main() { scanf("%d", &n); for (int i = 1; i <= n; ++i) points[i].read(); std::sort(points + 1, points + 1 + n, [&](const Point& a, const Point& b) { return a.x < b.x || (a.x == b.x && a.y < b.y); }); res = 1e20; solve(1, n); printf("%.0lf\n", res*res); return 0; }
同類型的習題可以參考上述的 平面最近點對 的連結內的習題
其它習題:
CF429D Tricky Function
題目描述:給定長度為\(n\)的序列\(a\),定義函式\(f(i , j) = (i - j)^2 + g(i , j)^2\) , 其中\(g(i , j) = preSum_i - preSum_j\)
求\(min_{i \neq j} f(i , j)\)
\[2 \leq n \leq 10^5 , |a_i| \leq 10^4 \]思路:根據題面如果把下標和對應的字首表示成點\((i , preSum_i)\),那麼問題就是求任意兩點距離的平方的最小值,套用上述模板即可
參考程式碼:
#include<bits/stdc++.h>
struct Point {
double x, y;
void read() {
scanf("%lf%lf", &x, &y);
return;
}
Point(double _x = 0.0, double _y = 0.0) :x(_x), y(_y) {}
bool operator < (const Point& a) const {
return y < a.y;
}
};
double res;
void update(const Point& a, const Point& b) {
double dx = sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
res = std::min(res, dx);
return;
}
const int N = 1e6 + 5;
Point points[N], t[N];
void solve(int lr, int rs) {
if (lr >= rs) return;
if (rs - lr == 1) {
update(points[lr], points[rs]);
std::sort(points + lr, points + rs + 1);
return;
}
int mid = lr + rs >> 1;
double midx = points[mid].x;
solve(lr, mid); solve(mid + 1, rs);
int l = lr, r = mid + 1, k = 0;
while (l <= mid && r <= rs) {
if (points[l] < points[r]) t[++k] = points[l++];
else t[++k] = points[r++];
}
while (l <= mid) t[++k] = points[l++];
while (r <= rs) t[++k] = points[r++];
k = 1;
for (int i = lr; i <= rs; ++i) points[i] = t[k++];
int size = 0;
for (int i = lr; i <= rs; ++i) {
if (std::abs(points[i].x - midx) < res) {
for (int j = size - 1; j >= 0 && points[i].y - t[j].y < res; --j)
update(points[i], t[j]);
t[size++] = points[i];
}
}
return;
}
int n;
int a[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), a[i] += a[i - 1];
for (int i = 1; i <= n; ++i) {
points[i].x = i;
points[i].y = a[i];
}
std::sort(points + 1, points + 1 + n, [&](const Point& a, const Point& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
});
res = 1e20;
solve(1, n);
printf("%.0lf\n", res * res);
return 0;
}