Algorithm

[Algorithm] ์ตœ๊ทผ์ ‘ ์ ์˜ ์Œ ์•Œ๊ณ ๋ฆฌ์ฆ˜ (Closest Pair)

yevdev 2021. 10. 6. 10:38

๐Ÿ’ก์ตœ๊ทผ์ ‘ ์ ์˜ ์Œ (Closest Pair) ๋ฌธ์ œ?

  • 2์ฐจ์› ํ‰๋ฉด์ƒ์˜ n๊ฐœ์˜ ์ ์ด ์ž…๋ ฅ์œผ๋กœ ์ฃผ์–ด์งˆ ๋•Œ, ๊ฑฐ๋ฆฌ๊ฐ€ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ํ•œ ์Œ์˜ ์ ์„ ์ฐพ๋Š” ๋ฌธ์ œ

 

๐Ÿ’ก๊ฐ„๋‹จํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• : O(n^2) ์•Œ๊ณ ๋ฆฌ์ฆ˜

  • ๋ชจ๋“  ์ ์— ๋Œ€ํ•ด ๊ฐ๊ฐ์˜ ๋‘์  ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ ์˜ ์Œ ์ฐพ๊ธฐ
  • ๋น„๊ตํ—ค์•ผ ํ•  ์Œ์€ ๋ช‡๊ฐœ?
    • nC2 = n(n-1)/2 = O(n^2)
  • ํ•œ์Œ์˜ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ์€ O(1)
  • ๋”ฐ๋ผ์„œ ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” O(n^2)

 

๐Ÿ’ก๋ณด๋‹ค ํšจ์œจ์ ์ธ ํ•ด๊ฒฐ๋ฐฉ๋ฒ• : ๋ถ„ํ•  ์ •๋ณต ์ด์šฉ

  1. n๊ฐœ์˜ ์ ์„ 1/2๋กœ ๋ถ„ํ• ํ•˜์—ฌ ๊ฐ๊ฐ์˜ ๋ถ€๋ถ„ ๋ฌธ์ œ์—์„œ ์ตœ๊ทผ์ ‘ ์ ์˜ ์Œ ์ฐพ๊ธฐ
  2. 2๊ฐœ์˜ ๋ถ€๋ถ„ํ•ด ์ค‘์—์„œ ์งง์€ ๊ฑฐ๋ฆฌ (d)๋ฅผ ๊ฐ€์ง„ ์ ์˜ ์Œ์„ ์ผ๋‹จ ์ฐพ๊ธฐ (์žฌ๊ท€์ ์œผ๋กœ)
  3. ๊ฒฐํ•ฉ : ๋‘๊ฐœ์˜ ๋ถ€๋ถ„ํ•ด๋ฅผ ์ทจํ•ฉํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ๋‘ ๋ถ€๋ถ„ ๋ฌธ์ œ์„œ ๊ฐ๊ฐ ํ•œ ์  ์”ฉ ํฌํ•จ๋˜๋Š” ์ ์˜ ์Œ ์ค‘์—์„œ ๊ทธ ๊ฑฐ๋ฆฌ๊ฐ€ d๋ณด๋‹ค ์ž‘์€ ์Œ ์ฐพ๊ธฐ (์ค‘๊ฐ„ ์˜์—ญ ๊ณ ๋ ค)
  4. ๋งŒ์•ฝ d๋ณด๋‹ค ์ž‘์€ ์Œ์ด ๋‚˜์™”๋‹ค๋ฉด, ๊ทธ๋Ÿฐ ์Œ ์ค‘์— ๊ฐ€์žฅ ์งง์€ ์Œ์ด ํ•ด์ด๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ๊ฑฐ๋ฆฌ๊ฐ€ d์ธ ์Œ์ด ํ•ด์ด๋‹ค.

 

์ค‘๊ฐ„ ์˜์—ญ์— ์žˆ๋Š” ์Œ๋“ค์„ ๊ฒ€์ƒ‰ํ•˜๋Š” ๋ฐฉ๋ฒ•

  • ์ ๋“ค์˜ ์ง‘ํ•ฉ์„ S๋ผ๊ณ  ํ•˜๊ณ , S๋ฅผ ๋‘˜๋กœ ๋‚˜๋ˆˆ ์ง‘ํ•ฉ์„ SL, SR ์ด๋ผ๊ณ  ํ•˜์ž
  • SL, SR์—์„œ ๊ฐ๊ฐ ์ฐพ์€ closest pair์„ ๊ฐ๊ฐ CPL, CPR ์ด๋ผ๊ณ  ํ•˜๊ณ , ๋‘์  ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ๋ฅผ dist()ํ•จ์ˆ˜๋กœ ํ‘œ์‹œํ•˜๋ฉด
    • d = min(dist(CPL), dist(CPR)) = min{10, 15} = 10
  • ๊ทธ๋Ÿฌ๋ฉด, ๋ถ„ํ• ํ•œ ์ง€์ ์—์„œ X์ถ•์œผ๋กœ +-d (+-10) ์ธ ๋ฒ”์œ„์— ์†ํ•œ ์ ๋“ค๋งŒ ๊ฒ€์ƒ‰ํ•ด๋„ ์ถฉ๋ถ„
  • x์ขŒํ‘œ์˜ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ ๋“ค์ด ์ •๋ ฌ๋˜์–ด ์žˆ๋‹ค๋ฉด, ์ค‘๊ฐ„์˜์—ญ ๊ณผ์ •์—์„œ ๊ฒ€์ƒ‰ํ•  ์ ๋“ค์„ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ก ์ตœ๊ทผ์ ‘ ์ ์˜ ์Œ ๋ถ„ํ•  ์ •๋ณต ์•Œ๊ณ ๋ฆฌ์ฆ˜

  1. if (i<=3) return (2๊ฐœ ๋˜๋Š” 3๊ฐœ์˜ ์  ์‚ฌ์ด์˜ ์ตœ๊ทผ์ ‘ ์Œ)
  2. ์ •๋ ฌ๋œ S๋ฅผ ๊ฐ™์€ ํฌ๊ธฐ์˜ SL๊ณผ SR๋กœ ๋ถ„ํ• 
    1. |S|๊ฐ€ ํ™€์ˆ˜์ด๋ฉด, |SL| = |SR| + 1 ์ด ๋˜๋„๋ก ๋ถ„ํ• 
  3. CPL = ClosestPair(SL) //CPL = SL์—์„œ์˜ ์ตœ๊ทผ์ ‘ ์ ์˜ ์Œ
  4. CPR = ClosestPair(SR) //CPR = SR์—์„œ์˜ ์ตœ๊ทผ์ ‘ ์ ์˜ ์Œ
  5. d = min{dist(CPL), dist(CPR)} ์ผ๋•Œ, ์ค‘๊ฐ„ ์˜์—ญ์— ์†ํ•˜๋Š” ์ ๋“ค ์ค‘์—์„œ ์ตœ๊ทผ์ ‘ ์ ์˜ ์Œ์„ ์ฐพ์•„์„œ ์ด๋ฅผ CPC๋ผ๊ณ  ํ•˜์ž.
  6. return (CPL, CPR, CPC ์ค‘์— ๊ฐ€์žฅ ๊ฑฐ๋ฆฌ๊ฐ€ ์งง์€ ์Œ)

 

์˜ˆ์ œ ์•Œ๊ณ ๋ฆฌ์ฆ˜

์œ„์˜ ์‚ฌ์ง„์— ์žˆ๋Š” ๋ชจ๋“  ์ ๋“ค์˜ ์Œ์ค‘ ๊ฐ€์žฅ ์ตœ๊ทผ์ ‘ ์ ์˜ ์Œ์„ ์ฐพ์•„๋ณด์ž

 

 

โ—๏ธ ์ค‘๊ฐ„์˜์—ญ์—์„œ CPC์„ ์ฐพ์„ ๋•Œ, SL๊ณผ SR์ด  ๋ชจ๋‘ ์ค‘๊ฐ„์˜์—ญ์— ์žˆ์„ ๊ฒฝ์šฐ? O(n^2)

  • Brute forceํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ closest pair๋ฅผ ๊ตฌํ•œ๋‹ค๋ฉด O(n^2) -> ์•„๋ฌด๋Ÿฐ ์ด๋“์„ ๋ณผ ์ˆ˜ ์—†์Œ
  • for(i=1;i<=n;i++){
    	for(j=i+1;j<=n;j++){
        	d(ij) = dist(pi,pj)๋ฅผ ๊ตฌํ•˜๊ณ , d(ij)<d ์ด๋ฉด, d=d(ij)
            }
    }โ€‹
  • ์ค‘๊ฐ„ ์˜์—ญ์— ์žˆ๋Š” ์ ๋“ค์„ y์ขŒํ‘œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์˜ค๋ฆ„์ฐจ์ˆœ ๋˜๋Š” ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜์ž
  • -> y ์ถ•์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌํ•œ ์ ๋“ค์„ p1, p2, ... pn์ด๋ผ๊ณ ํ•˜์ž.
  • ์  pi์˜ y์ขŒํ‘œ๋ฅผ yi ๋ผ๊ณ ํ•˜์ž. ์ ๋“ค์ด y ๋ฐฉํ–ฅ์œผ๋กœ ์ •๋ ฌ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์ด ๋ณด๋‹ค ํšจ์œจ์ ์ด๋‹ค.
    • for(i=1;i<=n-1;i++)
      	for(j=1+1;j<=n;j++)
          	if(yi-yj>=d) break;
              d(ij) = dist(pi, pj)๋ฅผ ๊ตฌํ•˜๊ณ , d(ij)<d ์ด๋ฉด d=d(ij)
      ๊ทธ๋Ÿฌ๋‚˜, ์œ„ ๊ณผ์ •์€ d ๊ฐ’์— ๋”ฐ๋ผ ๊ทธ ์†๋„๊ฐ€ ๊ฒฐ์ •๋œ๋‹ค.

 

๐Ÿ’ก์‹คํ–‰์ฝ”๋“œ C++

#include <iostream>
#include <vector> // ๋ฒกํ„ฐ ๋ฆฌ์ŠคํŠธ์‹œ ์‚ฌ์šฉ
#include <utility> // ์ž…๋ ฅ๊ฐ’ -> ๋ฒกํ„ฐ๋ฆฌ์ŠคํŠธ ํ•œ์Œ์œผ๋กœ
#include <cmath> // sqrt ์‚ฌ์šฉ
#include <sstream> // ์ž…๋ ฅ๊ฐ’ ์ถ”์ถœ ์‹œ ์‚ฌ์šฉ

#define X first
#define Y second

// ์ž…์ถœ๋ ฅ ์†๋„ ๊ฐœ์„ 
#define IOFAST ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

using namespace std;
vector<pair<long long, long long> > list;

// ์ž…๋ ฅ ์ถ”์ถœ ๋ฐ ๋ฌธ์ž์—ด -> ์ˆซ์ž ๋ณ€๊ฒฝ ํ•จ์ˆ˜
void parseInt(string tmp) {
    stringstream ss(tmp);
    vector<long long> temp;
    for (int i; ss >> i;) {
        temp.push_back(i);
        if (ss.peek() == ',' || ss.peek() == ' ')
            ss.ignore();
    }
    list.push_back(make_pair(temp[0], temp[1]));
}

void quickSortX(int start, int end){
    if(start >= end) return;
    int key = start;
    int i = start + 1, j = end;
    pair<long long, long long> temp;

    while (i<=j){
        while(i<=end && list[i].X <= list[key].X) i++;
        while(j>start && list[j].X >= list[key].X) j--;
        if(i>j){
            temp = list[j];
            list[j] = list[key];
            list[key] = temp;
        } else {
            temp = list[i];
            list[i] = list[j];
            list[j] = temp;
        }
    }
    quickSortX(start, j-1);
    quickSortX(j+1, end);
}

void quickSortY(int start, int end){
    if(start >= end) return;
    int key = start;
    int i = start + 1, j = end;
    pair<long long, long long> temp;

    while (i<=j){
        while(i<=end && list[i].Y <= list[key].Y) i++;
        while(j>start && list[j].Y >= list[key].Y) j--;
        if(i>j){
            temp = list[j];
            list[j] = list[key];
            list[key] = temp;
        } else {
            temp = list[i];
            list[i] = list[j];
            list[j] = temp;
        }
    }
    quickSortY(start, j-1);
    quickSortY(j+1, end);
}

double Dist(pair<long long, long long> x, pair<long long, long long> y){
    return sqrt((x.X-y.X) * (x.X-y.X) + (x.Y-y.Y) * (x.Y-y.Y));
}

double CPCCheck(int left, int right, double d){
    quickSortY(left, right);
    for(int i=left; i<=right; ++i){
        for(int j=i+1; j<=right; ++j){
            if(list[j].Y-list[i].Y>d) break;
            double tmp = Dist(list[i], list[j]);
            d = d < tmp ? d : tmp;
        }
    }
    return d;
}

double ClosestPair(int left, int right){
    int dots = right-left+1;
    double min;

    if(dots == 2){ // ์ ์ด ๋‘์ ์ผ๋• ๊ทธ๋ƒฅ ๊ตฌํ•˜๋ฉด ๋จ
        return Dist(list[left], list[right]);
    } else if(dots == 3){ // 3์ ์ผ๋•Œ 3์ ์‚ฌ์ด ๊ฑฐ๋ฆฌ
        double a = Dist(list[left], list[left + 1]);
        double b = Dist(list[left + 1], list[left + 2]);
        double c = Dist(list[left+2],list[left]);
        if(a<b){
            if(a<c){
                return a;
                }
                return c;
                }
        else{
            if(b<c){
                return b;
            }
            return c;
        }
    } else {
        int mid = (left + right) / 2;
        double a = ClosestPair(left, mid);
        double b = ClosestPair(mid+1, right);
        min = a < b ? a : b;
        return CPCCheck(left, right, min);
    }
}

int main() {
    IOFAST;
    int dots;
    cin >> dots;
    cin.ignore();
    for(int i=0; i<dots; ++i){
        string s;
        // ์ค„ ๋ฐ”๊ฟˆ ๊นŒ์ง€ ์ž…๋ ฅ ๋ฐ›๊ธฐ
        getline(cin, s);
        parseInt(s);
    }
    quickSortX(0, list.size());

    // for ์†Œ์ˆ˜์  6์ž๋ฆฌ ์ถœ๋ ฅ
    cout << fixed;
    cout.precision(6);

    cout << ClosestPair(0, list.size());
}

 


๐Ÿ’ก์‹œ๊ฐ„ ๋ณต์žก๋„

์ž…๋ ฅ s์— n๊ฐœ์˜ ์ ์ด ์žˆ๋‹ค๋ฉด,

  • ์ „์ฒ˜๋ฆฌ ๊ณผ์ •์œผ๋กœ์„œ s์˜ ์ ์„ x-์ขŒํ‘œ๋กœ ์ •๋ ฌ : O(nlogn)
  • s์— 3๊ฐœ์˜ ์ ์ด ์žˆ๋Š” ๊ฒฝ์šฐ์— 3๋ฒˆ์˜ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ์ด ํ•„์š”ํ•˜๊ณ , s์˜ ์ ์˜ ์ˆ˜๊ฐ€ 2์ด๋ฉด 1๋ฒˆ์˜ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ์ด ํ•„์š”ํ•˜๋ฏ€๋กœ O(1)์˜ ์‹œ๊ฐ„
  • ์ •๋ ฌ๋œ s๋ฅผ sl๊ณผ sr๋กœ ๋ถ„ํ•  ํ•˜๋Š”๋ฐ, ์ด๋ฏธ ๋ฐฐ์—ด์— ์ •๋ ฌ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ๋ฐฐ์—ด์˜ ์ค‘๊ฐ„ ์ธ๋ฑ์Šค๋กœ ๋ถ„ํ•  : O(1)์˜ ์‹œ๊ฐ„
  • sl๊ณผ sr์— ๋Œ€ํ•˜์—ฌ ๊ฐ๊ฐ ClosestPair์„ ํ˜ธ์ถœํ•˜๋Š”๋ฐ, ๋ถ„ํ• ํ•˜๋ฉฐ ํ˜ธ์ถœ๋˜๋Š” ๊ณผ์ •์€ ํ•ฉ๋ณ‘์ •๋ ฌ๊ณผ ๋™์ผ
  • (1) d = min{dist(cpl), dist(cpr)}์ผ ๋•Œ, ์ค‘๊ฐ„ ์˜์—ญ์— ์†ํ•˜๋Š” ์ ๋“ค ์ค‘์—์„œ ์ตœ๊ทผ์ ‘ ์ ์˜ ์Œ์„ ์ฐพ๋Š”๋‹ค.
    • y ์ขŒํ‘œ๋กœ ์ •๋ ฌํ•˜๋Š” ์‹œ๊ฐ„ : O(nlogn)
    • ๊ทธ๋‹ค์Œ ์•„๋ž˜์—์„œ ์œ„๋กœ ์˜ฌ๋ผ๊ฐ€๋ฉฐ ๊ฐ ์ ์—์„œ ์ฃผ๋ณ€์˜ ์ ๋“ค ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ„์‚ฐํ•˜๋Š”๋ฐ O(1)์˜ ์‹œ๊ฐ„
      • ๊ฐ ์ ๊ณผ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ„์‚ฐํ•ด์•ผํ•˜๋Š” ์ฃผ๋ณ€ ์ ๋“ค์˜ ์ˆ˜๋Š” O(1)๊ฐœ ์ด๊ธฐ ๋•Œ๋ฌธ!
  • (2) cpl, cpc, cpr ์ ์˜ ์Œ์ค‘์— ๊ฐ€์žฅ ์งง์€ ๊ฑฐ๋ฆฌ๋ฅผ ๊ฐ€์ง„ ์ ์˜ ์Œ์„ ๋ฆฌํ„ด :  O(1)์˜ ์‹œ๊ฐ„
  • k์ธต๊นŒ์ง€ ๋ถ„ํ•  ๋œ ํ›„, ์ธต๋ณ„๋กœ (1), (2) ์ด ์ˆ˜ํ–‰๋˜๋Š” combine (์ทจํ•ฉ) ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜๋ฏ€๋กœ
    • ClosestPair ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ์‹œ๊ฐ„๋ณต์žก๋„ = O(nlog(^2)n)
      • ๊ฐ์ธต์˜ y์ขŒํ‘œ ์ •๋ ฌ์˜ ์ˆ˜ํ–‰์‹œ๊ฐ„์€ O(nlogn)
      • ์—ฌ๊ธฐ์— ์ธต ์ˆ˜์ธ logn

โž• O(nlogn)์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ๊ฐœ์„ ํ•˜๊ธฐ

  • ์ž…๋ ฅ์˜ ์ ๋“ค์„ y ์ขŒํ‘œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ „์ฒ˜๋ฆฌ ๊ณผ์ •์—์„œ ๋ฏธ๋ฆฌ ์ •๋ ฌํ•˜๊ธฐ
  • double ClosestPair(int left, int right){
        int dots = right-left+1;
        double min;
    
        if(dots == 2){
            return distance(list[left], list[right]);
        } else if(dots == 3){
            double a = distance(list[left], list[left + 1]);
            double b = distance(list[left + 1], list[left + 2]);
            return a < b ? a : b;
        } else {
            int mid = (left + right) / 2;
            double a = ClosestPair(left, mid);
            double b = ClosestPair(mid+1, right);
            min = a < b ? a : b;
            return CheckBand(left, right, min);
        }
    }
  • ์ด ์ฝ”๋“œ์—์„œ if, else if๋ฌธ์—๋Š” ์ •๋ ฌ์„ ๊ทธ๋ƒฅ ๋„ฃ์–ด์คŒ -> 2, 3 ๊ฐœ๋‹ˆ๊นŒ ๊ทธ๋ƒฅ O(1)์œผ๋กœ ์ •๋ ฌ
  • else ๋ฌธ์—๋Š” mergesort๋ฅผ ๋„ฃ์–ด์คŒ -> ์ด๋ฏธ ์ •๋ ฌ๋œ 2๊ฐœ, 3๊ฐœ ์งœ๋ฆฌ -> O(n)

๐Ÿ’ก์‘์šฉ

  • ์ปดํ“จํ„ฐ ๊ทธ๋ž˜ํ”ฝ์Šค
  • ์ปดํ“จํ„ฐ ๋น„์ „
  • ์ง€๋ฆฌ์ •๋ณด ์‹œ์Šคํ…œ
  • ๋ถ„์ž ๋ชจ๋ธ๋ง
  • ํ•ญ๊ณต ํŠธ๋ž˜ํ”ฝ ์กฐ์ •
  • ๋งˆ์ผ€ํŒ… (์ฃผ์œ ์†Œ, ํ”„๋žœ์ฐจ์ด์ฆˆ ์‹ ๊ทœ ๊ฐ€๋งน์  ๋“ฑ์˜ ์œ„์น˜ ์„ ์ • ๋“ฑ)

 


 

Reference

์„œ์šธ๊ณผํ•™๊ธฐ์ˆ ๋Œ€ํ•™๊ต ์ปดํ“จํ„ฐ๊ณตํ•™๊ณผ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ˆ˜์—