라인스위핑, 투포인터

2025. 10. 6. 18:02·Develop/Algorithm

라인스위핑(line sweeping)

빗자루 쓸 듯 하나의 라인을 탐색하는 알고리즘입니다. 

보통 집합이나 교차점과 같은 기하 문제를 풀 때 사용되곤 하지만, 코딩테스트에서는 그 정도의 난이도로는 나오지 않습니다.

따라서 보통 최대 구간, 횟수 등을 찾는데 많이 쓰이는 알고리즘입니다.

 

예제 문제

철수는 도화지 위에 여러 번 선을 긋습니다.

  • 한 선은 두 점 (a, b)로 표현됩니다.
  • 여러 선이 겹쳐 그려지더라도, 겹친 구간은 한 번만 계산해야 합니다.

즉, 모든 선분을 합쳤을 때 총 길이를 구하는 문제입니다.

 

입력 조건

  • 첫 줄: 선의 개수 N (1 ≤ N ≤ 1,000,000)
  • 이후 N개의 줄: 두 정수 a, b (-1,000,000,000 ≤ a ≤ b ≤ 1,000,000,000)

출력 조건

  • 겹치지 않게 계산한 선분의 총 길이

 

예제 입력

5
0 2
1 5
3 7
8 10
6 9

 

예제 출력

10

 

해당 문제를 단순하게 풀려면 선의 길이에 해당하는 포인트들을 마킹하는 방식이 있습니다.
예를 들어 1~3 의 선분이라면 mark[1] = mark[2] = mark[3] = 1; 과 같은 방식으로요.
하지만 해당 방식은 결국 20억가량의 모든 점을 체크해야하니 시간초과도 날 뿐더러 메모리 초과도 납니다.

따라서 이럴 땐 라인스위핑을 이용합니다. 입력을 받을 때마다 선분을 늘려두거나, 선분의 길이를 바로 ret에 계산해둡니다.
아래의 정답 코드로 바로 보시죠!

 

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> P;
P L[1000004];
int n, from, to, l, r, ret;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> from >> to;
        L[i] = P(from, to);
    }

    sort(L, L + n); // 시작점 기준 정렬

    l = L[0].first, r = L[0].second;
    for (int i = 1; i < n; i++) {
        if (r < L[i].first) { // 안 겹침
            ret += (r - l);
            l = L[i].first;
            r = L[i].second;
        } else if (L[i].second > r) { // 겹침
            r = L[i].second;
        }
    }
    ret += (r - l);

    cout << ret << '\n';
}

 


투포인터(two pointer)

투포인터는 '정렬된 배열(선형 자료구조)'에서 2개의 포인터를 사용하여 특정 조건을 만족하는 부분 배열이나 값을 찾을 때 주로 사용되는 알고리즘입니다. 보통 정렬된 배열에서 두 수의 합 찾기, 연속된 부분배열의 합 찾기에 사용됩니다.

 

투포인터 알고리즘은 말 그대로 Left Pointer, Right Pointer 를 이용합니다. 

왼쪽 포인터(Left Pointer) : 배열의 시작 지점 -> 오른쪽으로 이동

오른쪽 포인터(Right Pointer) : 배열의 끝 지점 -> 왼쪽으로 이동

 

예제 문제

영희는 숫자를 좋아해서, 주어진 수열에서 두 수의 합이 X가 되는 쌍의 개수를 찾고자 합니다.

 

입력 조건

  • 첫 줄: 수열의 크기 n
  • 둘째 줄: 수열의 원소 (서로 다른 양의 정수, 1 ≤ a[i] ≤ 100,000)
  • 셋째 줄: 정수 X (1 ≤ X ≤ 200,000)

출력 조건

  • 합이 X가 되는 쌍의 개수

예제 입력

8
1 9 2 8 4 7 3 6
10

 

예제 출력

4

 

해당 문제도 nC2 를 하면 되므로 이중 반복문으로 풀어낼 수 있습니다. 하지만 n이 매우 클 경우 시간초과가 납니다.
시간 복잡도 자체가 O(n^2)이기 때문입니다.

따라서 이 문제를 해결하기 위해, 정렬 후 투포인터를 적용합니다.
정렬해둔뒤, 좌-우 포인터를 움직여가며 조건에 만족하는 값이 있는지를 판단합니다.

그럼 정렬하는데 걸리는 시간인 O(nlogn) + 탐색하는데 걸리는 시간인 O(n)이 걸리므로,
최종적으로 max(nlogn,n) ~= 보통 O(nlogn) 의 시간 복잡도를 갖게 됩니다.

정답 코드는 아래와 같습니다.

 

정답 코드

#include <bits/stdc++.h>
using namespace std;

int n, ret, x;

int main() {
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) cin >> a[i];
    cin >> x;

    sort(a.begin(), a.end());
    int l = 0, r = n - 1;

    while (l < r) {
        int sum = a[l] + a[r];
        if (sum == x) {
            ret++;
            r--; // 중복 방지 위해 r 이동
        } else if (sum > x) {
            r--;
        } else {
            l++;
        }
    }
    cout << ret << "\n";
}

 

'Develop > Algorithm' 카테고리의 다른 글

페이지 교체 알고리즘 (Page Replacement Algorithm)  (0) 2025.10.10
그리디 (Greedy)  (0) 2025.10.05
정렬 비교 함수 (compare)  (0) 2025.09.20
비트마스킹 (with 비트 연산자)  (1) 2025.09.11
진법과 보수  (1) 2025.09.11
'Develop/Algorithm' 카테고리의 다른 글
  • 페이지 교체 알고리즘 (Page Replacement Algorithm)
  • 그리디 (Greedy)
  • 정렬 비교 함수 (compare)
  • 비트마스킹 (with 비트 연산자)
ocahs
ocahs
개발 내용을 담습니다.
  • ocahs
    ocahs 개발 블로그
    ocahs
  • 전체
    오늘
    어제
    • 분류 전체보기 (47)
      • Develop (47)
        • Frontend (25)
        • Javascript (7)
        • Algorithm (14)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    번들러
    line sweeping
    비트 연산자 활용 예시
    JS
    promise reject
    Promise
    요청의 역사
    vite
    Working Set Model
    재귀타입
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
ocahs
라인스위핑, 투포인터
상단으로

티스토리툴바