OJ:P1429 平面最近点对(加强版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
非常详细的博客:平面上最近点对 - 洛谷专栏 (luogu.com.cn)
更正式的文章:平面最近点对 - OI Wiki
这也是我们算法课的一个实验。不过我做的不好,我只会简单的分治,中间区域合并时用的还是蛮力QAQ。
分治学好后,难点其实在合并这里的处理。(直接想极端情况吧,所有点的x都相同)
————————
图解总思路:
1.分治:
————
2.中间处理部分:
(注意我们是用序号排的序,二分也是根据序号二分)
这个中线可以是“mid点”
理解:
其实极端情况就是存在更近点对且mid离他们很远。注意这两个点的x和y都是很近的,距离小于d,而他们离mid的x也绝对都小于d,所以是在范围内的。
所以这个2d其实可以再缩减,mid向右d 与 mid+1向左d 的交集 ,如果是mid或者mid+1能有更近点对,那么这个d是足够的,再远点x都大于d了。
————
3.中间合并部分
对于每个左边的点比如P,最多检查右边这六个点。
其实最多五个,右边三个距离肯定大于d了。
但如何做到高效比较呢,只能将中间部分左右的所有点排个序,一起比了。
根据y坐标排序,
每个点比后面的点,直到纵坐标之差超过d。
排序O(nlongn) , 比较O(5n) (这里比较只比自己下面的,左边也可能下面也有一个d,见下图)
合并部分每次总的就是O(nlongn + n)
如果每次都这样的话,总的其实是 O( (nlogn+n) * logn )
可以进一步优化:
前面排好,直接给后面用。 可能会多循环一些点,但是消耗远比排序小。
(这样做就不是先找点,排序,比较了,而是排序,合法点,比较。注意第一层循环和mid的x距离超了就不用比了,不在范围内)
合并部分每次就是O(n)了
总的合并部分就是O(n*logn)了
————
总的时间复杂度:
排序加上分治的时间复杂度结果为 2nlongn + n ,即 nlongn
参考代码:
其实全程跟mindis比就可以。
#define ll long long
#define endl "\n"
//#define int long long
#define PII pair<int,int>
const int maxn = 100000;
struct Point
{
int x, y;
};
double mindis = DBL_MAX;
void distance(const Point& a, const Point& b)
{
mindis = min(mindis,sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)));
}
vector<Point>arr,arr2,tmp;
pair<Point, Point> closestPair;
void fmerge(int l, int r)
{
double dis = DBL_MAX;
if (l == r)
return;
else if (l + 1 == r)
{
if (arr2[l].y > arr2[r].y)
swap(arr2[l], arr2[r]);
return;
}
int m = l + ((r - l) >> 1);
fmerge(l, m);
fmerge(m + 1, r);
int al = l, bl = m + 1,tot = l;
while (al <= m && bl <= r)
{
if (arr2[al].y < arr2[bl].y)
tmp[tot++] = arr2[al++];
else
tmp[tot++] = arr2[bl++];
}
while(al <= m)
tmp[tot++] = arr2[al++];
while(bl <= r)
tmp[tot++] = arr2[bl++];
for (int i = l; i <= r; i++)
arr2[i] = tmp[i];
for (int i = l; i < r; i++)
{
//mid
if(abs(arr2[i].x - arr[m].x)<mindis)
for (int j = i + 1; j <=r && tmp[j].y - tmp[i].y <= mindis; j++)
{
distance(tmp[i], tmp[j]);
}
}
return;
}
void findTwoPointsWithTheShortestDistance()
{
int size = arr.size();
sort(arr.begin(), arr.end(), [&](const Point& a, const Point& b) {
if (a.x == b.x)
return a.y < b.y;//return a.y <= b.y;
return a.x < b.x;
}
);
arr2 = arr;
tmp = vector<Point>(arr.size());
fmerge(0, size - 1);
}
void solve()
{
int n;
cin >> n;
arr = vector<Point>(n);
for (int i = 0; i < n; i++)
{
int a, b;
cin >> a >> b;
arr[i] = { a,b };
}
findTwoPointsWithTheShortestDistance();
//cout << mindis << endl;
printf("%.4lf", mindis);
}
signed main()
{
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
solve();
return 0;
}