1. 程式人生 > 其它 >平面最近點對

平面最近點對

平面最近點對

問題描述:給你\(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;
}