오름차순으로 만드는 최소 횟수
문제 링크
난이도
- 골드 5
- dp의 특성상 푸는 코드는 짧았다.
- 코드트리에서 릴레이 하다가 푼 재밌는 문제
- 근데 어떻게 풀어야 하는지가 고민해야 했다.
- LIS를 구현해야 했는데 그게 어려웠다.
문제 요약
- n 까지의 숫자가 중복 없이 존재
- 정렬 되지 않은 상태
- 오름차순으로 만들기 위해 최소 이동 횟수를 구하라.
- 이동은 위치 교환이 아닌, 삽입 정렬처럼 해당 위치로 꽂아 넣는 형태이다.
접근
- 왼쪽부터 순서대로 오름차순을 만든다.
- 가장 간단한 방법으로 생각.
- 문제점은 1237456이면?
- 중간의 7때문에 456을 전부 왼쪽으로 넘기느라 이동 횟수가 추가된다.
- 그럼 7을 오른쪽으로 옮기면 되지 않나?
- 그걸 어떻게 판단하지?
- LIS(최장 부분 증가 수열)를 이용하기
- 이동은 무조건 이뤄진다. 이를 최소화 해야한다.
- 이동은 특정 위치와 교환이 아닌, 삽입 정렬처럼 꽂아 넣는방식이 핵심
- 그럼 오름차순으로 이뤄진 숫자를 제외하고 이동시키면 된다.
- 이를 최장 부분 증가 수열(LIS) 이라 한다.
- 이 LIS가 가장 긴 길이를 찾아 n에서 빼면 그게 이동횟수가 된다.
간단 알고리즘
- LIS를 찾는 알고리즘
- 핵심은 오름차순으로 부분적으로 증가하는것을 찾는거.
- 현위치를 기준으로 왼쪽것들중 작은것의 갯수를 세면 된다.
- 그냥 세면 안되고! 점차 증가하는것만 세면 된다.
- dp의 정의를 잘 하면 된다.
-
dp
- dp[i]는 i이하 까지의 LIS를 저장
- 초기는 1로 초기화
- i를 0부터 n까지 반복
- j는 0부터 i-1까지 반복
- data[j] < data[i]로 왼쪽에서 나보다 낮은 LIS를 찾는다.
- dp[i] < dp[j]+1로 기존에 갱신된 LIS보다 높을때만 갱신한다.
- dp[i] = dp[j]+1로 갱신한다.
- dp[i] < dp[j]+1로 기존에 갱신된 LIS보다 높을때만 갱신한다.
- data[j] < data[i]로 왼쪽에서 나보다 낮은 LIS를 찾는다.
- j는 0부터 i-1까지 반복
후기
- LIS를 구현하는게 어려운 문제였다.
- 오른쪽을 확인해야 할지, 왼쪽을 확인 해야 할지
- 적당한 설계를 하기 고민했다.
- dp의 특성상 코드는 짧았다.