프로그래밍/Algorithm

백준 2568번 풀이

fast99 2023. 3. 24. 00:02
반응형

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

 

2568번: 전깃줄 - 2

첫째 줄에는 두 전봇대 사이의 전깃줄의 개수가 주어진다. 전깃줄의 개수는 100,000 이하의 자연수이다. 둘째 줄부터 한 줄에 하나씩 전깃줄이 A전봇대와 연결되는 위치의 번호와 B전봇대와 연결

www.acmicpc.net

문제 풀이

이 문제를 분석하여 좀 더 일반적인 상황으로 바꿔보면.. 결국 A가 정렬되어 있을때, 연결된 B 배열이 정렬되어 있어야 교차가 일어나지 않는 것을 알 수 있다. 

B 배열을 정렬하기 위해 최소한의 원소를 제거하여 B배열을 정렬해야한다. 말을 바꿔서 해보면... B배열에서 최대 길이의 정렬된 원소집합을 찾아야한다. 따라서... 이 문제는 LIS(최장 부분 수열)문제라는 것을 알 수 있다.

LIS문제의 일반적인 해결책인 dp를 통한 해결은 시간 복잡도가 O(N^2)이다. N의 제한은 100,000이므로 N^2알고리즘으로 설계하면 시간내에 문제를 해결할 수 없다. 따라서 최소한 O(NlogN) 시간복잡도로 해결해야한다.

문제를 요리조리 보며 해결하려고 했지만.... 도저히 방법이 떠오르지 않았다. 탐색을 logN 에 해야하기때문에.. 이진 탐색인거 같긴했으나 내가 원래 설계한 O(N^2) dp배열에서 이진탐색을 수행하려고 하니 도저히 방법이 떠오르지 않았다.

그래서 구글링하여 O(NlogN) LIS해결법을 찾았다.

https://4legs-study.tistory.com/106 (감사합니다.)

이 해결법을 보고 배운점은 dp 방식을 바꿀 수도 있다는 것이다. 혼자 힘으로 이진탐색을 써야한다는 것까지 유추했으나.. 더 이상 방법을 떠올리지 못했다. 이진탐색이 가능하게 하기 위하여 dp배열을 정렬된 형태로 변형하여 새롭게 점화식 규칙을 만드는 시도를 했어야하는데.. 그렇게 할 생각을 아예 못했다. 발상이 떠올랐다면 최대한 그 쪽으로 구현하려고 설계해보자.

#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
#include <algorithm>

using namespace std;

typedef struct line
{
    int a,b;
}   line;

bool operator<(const line &l1, const line &l2)
{
    return l1.a < l2.a;
}

int dp[100001];
int idx[100001];

int rec[100001];
line ln[100000];
int islis[500001];

int main()
{
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    std::ios_base::sync_with_stdio(false);

    int N;
    cin >> N;
    for (int i = 0 ; i < N ; ++i)
        cin >> ln[i].a >> ln[i].b;
    
    sort(ln, ln + N);

    int max_len = 0;
    vector<int> del;
    for (int i = 0 ; i < N ; ++i)
    {
        int k = lower_bound(dp, dp + max_len + 1, ln[i].b) - dp; 
        if (k > max_len)
        {
            idx[k] = i;
            dp[k] = ln[i].b;
            rec[i] = k;
            ++max_len;
        }
        else
        {
            idx[k] = i;
            dp[k] = ln[i].b;
            rec[i] = k;
        }
    } 
    cout << N - max_len << "\n";

    int cur = rec[idx[max_len]];
    islis[idx[max_len]] = 1;
    for (int i = idx[max_len] ; i >= 0 ; --i)
    {
        if (cur - 1 == rec[i])
        {
            cur = rec[i];
            islis[i] = 1;
        }
    }

    for (int i = 0 ; i < N ; ++i)
    {
        if (!islis[i])
            cout << ln[i].a << "\n";
    }
}

메모리: 5928 KB, 시간: 36 ms