본문 바로가기

BOJ

[BOJ 2261] 가장 가까운 두 점

https://www.acmicpc.net/problem/2261

 

2261번: 가장 가까운 두 점

첫째 줄에 자연수 n(2 ≤ n ≤ 100,000)이 주어진다. 다음 n개의 줄에는 차례로 각 점의 x, y좌표가 주어진다. 각각의 좌표는 절댓값이 10,000을 넘지 않는 정수이다. 여러 점이 같은 좌표를 가질 수도

www.acmicpc.net


Tag : Sweeping

 

풀이

\(N^2\)개의 점 중에 가장 가까운 두 점을 구해야하는 문제인데 \(\binom{N}{2}\)의 경우를 모두 따져보기엔 \(N\)이 굉장히 크다. 적어도 \(O(N\sqrt{N})\), 되도록 \(O(NlogN)\)까지 만들면 좋을 듯하다. 일단 \(N\)개의 점을 \(X\)기준으로 정렬해보자. 정렬한 후 모든 점을 순회하며 \(std::set\)에 현재 점과 \(X\)좌표의 차이가 현재까지의 최솟값 \(D\)보다 작은 점들만 가지고 있을 것이다. 이후 현재 점에서 \(Y\)좌표의 차이가 현재 최소인 \(D\)이하인 구간을 탐색할 것이다. 이 구간의 하한과 상한은 이분탐색을 통해 구할 수 있다.

(\(std::set::lower\_bound, std::set::upper\_bound)\) 

구한 구간의 모든 점과 현재 점의 거리로 최솟값을 갱신한다.

 

시간복잡도 : \(O(NlogN)\)

 

이 풀이가 왜 \(O(NlogN)\)에 돌아가는지 생각해보자. 스위핑을 진행하다 아무 점에서나 멈춰보자. 그럼 현재까지 갱신된 최소거리가 \(D\)이기 때문에 \(std::set\) 안에 들어있는 점들의 거리도 모두 각각 \(D\) 이상이다. 그리고 이분탐색으로 현재 점과의 \(Y\) 차이가 \(D\) 이하인 구간을 구하면 다음과 같은 구간이 나온다.

\(X\)거리가 \(D\)보다 큰 점들은 모두 제외됐고, \(Y\)거리가 \(D\)보다 큰 점들도 모두 제외됐기 때문에

위와 같은 직사각형에 속하는 점들만이 구간에 속하게 된다. 이 \(D*2D\)직사각형에 거리가 \(D\) 이상인 점들을 최대로 집어넣어봤자 5개정도가 나온다고 한다. 엄밀한 증명은 못하겠는데 넣어보려하면 더 넣긴 힘들다.

때문에 \(std::set\)에서 원소를 제거하는 것은 총 \(O(NlogN)\), 스위핑하는 원소의 개수는 \(O(N)\), 마지막으로 스위핑하는 원소마다 이분탐색을 하니 \(O(logN)\). 총합하면 \(O(NlogN)\)을 얻을 수 있다.

 

+ 솔직히 나는 못풀었는데 리프가 어거지로 풀이를 우겨넣어줘서 이해했다

 

전체 코드

#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define all(x) ((x).begin()), ((x).end())
#define endl '\n'
using namespace std;
using ll = long long;

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);

    int N;cin>>N;vector<pair<int,int>>v(N);
    for(auto &[i,j]:v)cin>>i>>j;sort(all(v));
    int D=numeric_limits<int>::max();set<pair<int,int>>st;
    for(int i=0,p=0;i<N;i++){
        for(;p<i;p++){
            if((v[p].fi-v[i].fi)*(v[p].fi-v[i].fi)>D)st.erase({v[p].se,v[p].fi});
            else break;
        }
        int t=sqrt(D)+1;
        auto l=st.lower_bound({v[i].se-t,-1e9});
        auto r=st.upper_bound({v[i].se+t,1e9});
        for(auto it=l;it!=r;it++){
            D=min(D,(it->se-v[i].fi)*(it->se-v[i].fi)+(it->fi-v[i].se)*(it->fi-v[i].se));
        }
        st.insert({v[i].se, v[i].fi});
    }
    cout<<D<<endl;
}

'BOJ' 카테고리의 다른 글

[BOJ 16182] Dumae  (0) 2021.08.15
[BOJ 17469] 트리의 색깔과 쿼리  (0) 2021.08.14
[BOJ 2682] 최대 사이클 1  (0) 2021.08.12
[Boj 1947] 선물 전달  (0) 2020.11.19
[boj 1365] 꼬인 전깃줄  (0) 2020.10.05