가장 긴 증가하는 부분 수열 (LIS) - Python with. 백준 12015번

2025. 2. 19. 14:39· Coding/코딩테스트 공부

오늘은 유명한 유형 중 하나인 LIS 유형에 대해 정리해보고자 한다.

 

해당 유형을 안 지 얼마 되지 않았지만, 유형이 어느정도 정형화 되어있는 느낌이다. 해당 유형을 풀이하는 방법은 DP, 이분탐색 2가지가 있다.

 

DP로 풀이할 경우 O(N^2)의 시간복잡도를, 이분탐색으로 풀이할 경우 O(NlogN)의 시간복잡도를 가진다. 따라서 문제에 나와있는 조건을 잘 보고 시간복잡도를 따져서 알고리즘을 선택하는 것이 중요하다.

 

해당 유형은 가장 긴 증가하는 부분 수열의 길이까지만 구하거나, 더 나아가 부분 수열 자체를 출력해야하는 문제들이 있다.

 

해당 유형은 1,2편으로 나누어 작성하고자 하며, 해당 글(1편)에서는 가장 긴 증가하는 부분 수열의 길이까지만 구하는 문제를 다뤄보고자 한다.

 

우선 백준 12015번 문제를 통해 이해해보면, 문제를 푸는 단계는 아래와 같다.

  1. 배열을 순차적으로 탐색한다.
  2. 배열을 탐색하다가 해당원소가 LIS 배열의 가장 끝 원소보다 더 크다면, 뒤에 삽입한다.
  3. 그렇지 않다면, LIS 수열을 이루는 원소 중 탐색중인 원소보다 크거나 같은 원소를 처음 만나는 위치(-> lower_bound)를 찾고, 해당 위치에 원소를 삽입한다.
  4. → 이 과정에서 우리는 이분탐색을 사용한다. (해당 문제는 N이 최대 1,000,000 이므로 DP로는 풀이가 불가하다.)

 

1. 1단계 (2번째 원소 10)

- LIS 배열의 가장 끝 원소인 10보다 크므로, 맨 뒤로 삽입한다.

- 참고) 풀이방법마다 다르지만, 나는 lis리스트에 먼저 10을 넣어놓고 시작했다.

 

 

 

 

2. 2단계 (3번째 원소 10)

- LIS 배열의 가장 끝 원소인 10보다 작으므로, 이분탐색을 통해 LIS 배열의 원소들 중 숫자 10보다 크거나 같은 원소를 가장 처음 만나는 위치를 찾는다. 여기서는 10이 존재하는 위치 0이다.

- LIS의 첫번째 위치에 10이 삽입된다. (변화x)

 

 

3. 3단계 (4번째 원소 30)

- LIS 배열의 가장 끝 원소인 20보다 크므로, 맨 뒤로 삽입한다.

 

 

4. 4단계 (5번째 원소 20)

- LIS 배열의 가장 끝 원소인 30보다 작으므로, 이분탐색을 통해 LIS 배열의 원소들 중 숫자 20보다 크거나 같은 원소를 가장 처음 만나는 위치를 찾는다. 여기서는 20이 존재하는 위치 1이다.

- LIS의 두번째 위치에 20이 삽입된다. (변화x)

 

5. 5단계 (6번째 원소 50)

- LIS 배열의 가장 끝 원소인 30보다 크므로, 맨 뒤로 삽입한다.

 

 

lower_bound : 어떤 값 x보다 크거나 같은 값이 나오는 첫 위치입니다. python에서는 bisect_left()
upper_bound : 어떤 값 x보다 큰 값이 나오는 첫 위치입니다. python에서는 bisect_right()

 

 

이분 탐색으로 풀었을 때, 풀이코드는 아래와 같다.

이분 탐색 알고리즘을 실제 코드로 구현해서 풀 수도 있지만, 파이썬에서 이분탐색을 지원하는 bisect함수가 있어서 이걸 쓰면 훨씬 간단해진다. 

 

import sys
import bisect

input = sys.stdin.readline

n = int(input())
nums = list(map(int, input().split()))

lis = [nums[0]]

for i in range(1, n):
    target = nums[i]
    if lis[-1] < target:
        lis.append(target)
    else:
        idx = bisect.bisect_left(lis, target)
        lis[idx] = target

print(len(lis))

 

 

-> 이분 탐색도 구현한 코드

import sys

input = sys.stdin.readline

n = int(input())
nums = list(map(int, input().split()))

def binary_search(target):
    start, end = 0, len(lis)

    while start <= end:
        mid = (start + end) // 2
        if lis[mid] == target:
            return mid

        elif lis[mid] < target:
            start = mid + 1
        else:
            end = mid - 1

    return start



lis = [nums[0]]
for i in range(1, n):
    target = nums[i]

    if lis[-1] < target:
        lis.append(target)
    elif lis[-1] > target:
        idx = binary_search(target)
        lis[idx] = target

print(len(lis))

 

 

-> 해당 문제는 시간초과로 인해 dp로 풀 수 있지만, 만약 푼다면 코드는 아래와 같다. 

import sys

input = sys.stdin.readline

n = int(input())
nums = list(map(int, input().split()))
dp = [1] * n

for i in range(n):
    for j in range(i):
        if nums[i] > nums[j]:
            dp[i] = max(dp[i], dp[j] + 1)

a = max(dp)
res = []

for i in range(n-1, -1, -1):
    if dp[i] == a:
        res.append(nums[i])
        a -= 1

print(len(res))

 

 

 

이렇게 이분탐색으로 LIS의 길이를 푸는 방법을 알아보았는데, 첫번째 풀이대로하면 가장 긴 증가하는 부분 수열을 직접 구할 때 아래 예시처럼 문제가 생긴다. LIS의 길이뿐만 아니라 LIS 원소들을 알고 싶으면 어떻게 해야 할까? -> 이건 2편에서 다뤄보겠습니다!!

 

 

[예시]  

 

주어진 수열: 1 7 3 2 5 10 3

실제 정답: 1 2 5 or 1 2 3

첫번째 풀이의 결과물: 1 2 3 10

 

 

 


도움 받은 글

 

가장 긴 증가하는 부분 수열(LIS) 완전 정복 - 백준 파이썬

최근 들어, 알고리즘에 재미를 붙였다. 백준 문제를 풀면 문제 난이도마다 티어가 올라가는 재밌는 사이트(solved.ac)가 생겨서 뭔가 동기 부여되는 것 같다. 언어는 python을 사용하고 있다. (취준

seohyun0120.tistory.com

 

저작자표시 비영리

'Coding > 코딩테스트 공부' 카테고리의 다른 글

백준 19238번 문제 - BFS & 구현  (0) 2025.02.17
[이.코.테] chap13. DFS/BFS - 특정 거리의 도시 찾기  (0) 2022.07.29
[이.코.테] chap11. 그리디 - 만들 수 없는 금액  (1) 2022.07.20
[코드 리뷰] 자료구조&알고리즘 - 2강 실습(2) 리스트에서 원소 찾아내기  (0) 2022.06.28
[알고리즘(Python)/코테공부] 뒤집은 소수  (0) 2022.01.27
'Coding/코딩테스트 공부' 카테고리의 다른 글
  • 백준 19238번 문제 - BFS & 구현
  • [이.코.테] chap13. DFS/BFS - 특정 거리의 도시 찾기
  • [이.코.테] chap11. 그리디 - 만들 수 없는 금액
  • [코드 리뷰] 자료구조&알고리즘 - 2강 실습(2) 리스트에서 원소 찾아내기
주디(Junior developer)
주디(Junior developer)
Hello World!😀 Hi, I'm Judy🐰(Junior Developer)
주디(Junior developer)
주디는 언제나 당근을 원해🥕
주디(Junior developer)
전체
오늘
어제
  • 분류 전체보기 (82)
    • 연합동아리 (5)
      • 멋쟁이사자처럼🦁 (2)
      • UMC (0)
      • SOPT (3)
    • 프론트엔드 (3)
      • HTML + CSS + Javascript (3)
    • 백엔드 (11)
      • Django (2)
      • SpringBoot (8)
      • Infra (1)
    • Programming study (18)
      • JAVA (3)
      • Python (14)
    • Coding (41)
      • Baekjoon(백준) (32)
      • 자료구조 (1)
      • 코딩테스트 공부 (7)
      • 프로그래머스 (0)
      • 트러블슈팅 (1)
    • github (2)
    • CS (1)
      • 운영체제 (0)
      • Database (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • Python
  • 스프링부트
  • HTML
  • C
  • 리스트
  • JPA
  • SOPT
  • 코테
  • Baekjoon
  • 멋사
  • 자바
  • 트랜잭션
  • 백준
  • BFS
  • JavaScript
  • CSS
  • c언어
  • 변수
  • Java
  • 프로그래머스
  • SpringBoot
  • dfs
  • 웹 프로그래밍
  • 12015번
  • Dear_Santa
  • 만들 수 없는 금액
  • 장고
  • 파이썬
  • 에라토스테네스의 체
  • django

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
주디(Junior developer)
가장 긴 증가하는 부분 수열 (LIS) - Python with. 백준 12015번
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.