Coding Test/BaekJoon_C++

백준 1858 <기울기가 가장 큰 두 점> C++

JunOnJuly 2024. 4. 20. 00:07
728x90

 

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

 

1858번: 기울기가 가장 큰 두 점

평면상에 N개의 서로 다른 점들이 주어져 있을 때, 그 중 임의의 두 점을 지나는 직선의 기울기의 절댓값이 가장 큰 두 점을 찾고 싶다. 두 점의 좌표가 (x1, y1), (x2, y2) 라고 할 때, 기울기의 절댓

www.acmicpc.net


완전탐색으로 알고리즘을 짜면 시간초과가 걸리니 적당히 샘플링 할 방법을 찾아야합니다.

여기서 어느 점들을 골라 비교하면 될까를 생각하게 되는데, 결론적으로는 x 축을 기준으로 인접한 점들을 비교해주면 됩니다.

 

어떤 좌표군을 탐색하겠습니다.

우리는 해당 좌표군을 2진 탐색의 방식으로 가장 기울기의 절댓값이 큰 선분어떤 식으로 존재하는지 탐색하겠습니다.

 

x 축을 기준으로 시작과 끝에 있는 점을 잇고(1) 가운데에 있는 점을 시작과 끝에 있는 점과 이으면(2)(3)

(1) 의 기울기보다 (2) 와 (3) 중 하나의 기울기가 클 수 밖에 없습니다.   

위 논리를 기반으로 시작과 가운데, 가운데와 끝을 순차적으로 탐색하면,

결국 현재 탐색된 기울기의 절댓값의 최댓값해당 선분을 잇는 양 끝점을 시작과 끝으로 하는 탐색에서 어떠한 두 선분 중 하나보다 기울기의 절댓값이 작을 것임을 알 수 있습니다.

결국 이 탐색은 인접한 두 점이 남을 때 까지 행해지며 결국 인접한 두 점을 잇는 선분 중 기울기의 절댓값이 최대가 나오는 선분이 나온다는 사실을 알 수 있습니다.

 

그렇다면 단순히 인접한 두 점을 탐색하면 되냐면 예외가 있습니다.

만약 같은 기울기가 연속적으로 나온다면 가장 작은 A와 B 를 출력해야 한다는 조건을 만족하지 않을 수 있기 때문입니다. 때문에 기울기가 같고 연속적으로 나온 상황은 따로 처리해줘야 합니다.

또, 기울기의 절댓값이 같은 경우는 A 와 B 를 비교해 출력하도록 처리해줘야 합니다.

 

결과적으로 탐색하는 순차적으로 인접하는 점들을 탐색하되 예외만 처리해주면 되겠습니다.


#include <iostream>
#include <array>
#include <vector>
#include <algorithm>
#include <cmath>
#include <limits>
#include <set>

using namespace std;

// 정렬 함수
bool sort_idx(array<int, 3>& arr_1, array<int, 3>& arr_2) {
    // x 오름차순
    return arr_1[0] < arr_2[0];
}

int main(void) {
    // 입력
    int N;
    cin >> N;
    // 좌표 정보 벡터, { { 좌표, 번호 }, 활성화 }
    vector<array<int, 3>> idxs;
    for (int i = 0; i < N; i++) {
        array<int, 3> temp_arr;
        cin >> temp_arr[0] >> temp_arr[1];
        temp_arr[2] = i + 1;
        idxs.push_back(temp_arr);
    }
    // x 오름차순, y 오름차순으로 정렬
    sort(idxs.begin(), idxs.end(), sort_idx);
    // 최대 기울기, 해당 번호
    float max_incline = 0;
    int max_A, max_B, max_i;
    // x 좌표가 인접한 좌표끼리 기울기 계산
    for (int i = 0; i < N - 1; i++) {
        float incline = static_cast<float>(idxs[i + 1][1] - idxs[i][1]) / static_cast<float>(idxs[i + 1][0] - idxs[i][0]);
        // 기울기의 절댓값이 최대 기울기의 절댓값보다 크면
        if (abs(incline) > abs(max_incline)) {
            // 기울기 최댓값 최신화
            max_incline = incline;
            // 번호, 인덱스 최신화
            max_A = min(idxs[i + 1][2], idxs[i][2]);
            max_B = max(idxs[i + 1][2], idxs[i][2]);
            max_i = i + 1;
        }
        // 기울기의 절댓값이 최대 기울기의 절댓값과 같으면
        if (abs(incline) == abs(max_incline)){
            // 값이 같고 연속되는 인덱스이면
            if (incline == max_incline and max_i == i) {
                // max_A, max_B 후보, 중복 제거
                set<int> candid_set = { max_A, max_B, idxs[i][2], idxs[i + 1][2] };
                // 벡터로 치환
                vector<int> candid_vec(candid_set.begin(), candid_set.end());
                // 정렬
                sort(candid_vec.begin(), candid_vec.end());
                // 가장 작은 두 값 할당
                max_A = candid_vec[0];
                max_B = candid_vec[1];
            }
            // 절댓값이 같거나 값이 같은데 연속되지 않는 경우
            else {
                // x 축 기준으로 앞에 있는 점이 max_A 보다 번호가 작거나
                // x 축 기준으로 뒤에 있는 점이 max_A 보다 번호가 작거나
                // x 축 기준으로 앞에 있는 점이 max_A 와 같고 뒤에 있는 점이 max_B 보다 작거나
                // x 축 기준으로 뒤에 있는 점이 max_A 와 같고 앞에 있는 점이 max_B 보다 작으면
                if (idxs[i][2] < max_A or
                    idxs[i + 1][2] < max_A or
                    (idxs[i][2] == max_A and idxs[i + 1][2] < max_B) or
                    (idxs[i + 1][2] == max_A and idxs[i][2] < max_B)) {
                    // 작은 값은 max_A
                    max_A = min(idxs[i + 1][2], idxs[i][2]);
;                   // 큰 값은 max_B
                    max_B = max(idxs[i + 1][2], idxs[i][2]);
                }
            }
        }
    }

    cout << max_A << " " << max_B;
}

 

728x90