일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- SWEA
- 비트 연산자
- SSAFY웰컴킷
- Union
- 산술 연산자
- Java
- 비교 연산자
- Stream
- 프로그래머스
- 누적합
- 자료구조
- unionfind
- 삼성청년SW아카데미
- SSAFY입학
- 순위함수
- 연산자 우선순위
- 알고리즘
- linkedlist
- 덱
- 내부반복
- rank
- prefix sum
- 코딩교육
- 삼항 연산자
- 자바 연산자
- 서로소집합
- deque
- 논리 연산자
- SSAFY
- 연결리스트
- Today
- Total
개발하는 몽당연필
최장 증가 부분 수열 (LIS, Longest Increasing Subsequence) 본문
백준 문제 풀다가 LIS 문제가 나왔는데 구글의 도움을 받고 풀어서 ㅠㅠ 정리해보려고 한다🤗
🔍 최장 증가 부분 수열 (LIS)
- 주어진 수열에서 오름차순으로 정렬된 가장 긴 부분 수열을 찾는 문제
- 이 때 부분 수열은 연속적이거나, 유일할 필요는 없다
🔍 구현 방법
총 2가지 방식으로 구현할 수 있다.
- 동적 계획법을 활용한 풀이 → O(N^2)
- 이분 탐색을 활용한 풀이 → O(NlogN)
이 문제를 두가지 방식으로 해결해봅시다❕
https://www.acmicpc.net/problem/11053
11053번: 가장 긴 증가하는 부분 수열
수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이
www.acmicpc.net
1️⃣ 동적 계획법을 활용한 풀이
public class LIS1 {
static int N;
static int[] arr;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
arr = new int[N];
for (int i = 0; i < N; i++) {
arr[i] = sc.nextInt();
}
// 각 위치에서의 LIS를 저장할 1차원 dp 테이블을 정의한다.
int[] dp = new int[N];
// 최대 LIS의 값.
int max = 1;
// 첫 번째 원소부터 N번째 원소까지 dp 테이블의 값을 채워 나간다.
for (int i = 0; i < N; i++) {
// 우선 해당 위치를 본인의 길이(1)로 초기화한다.
dp[i] = 1;
// 현재 원소의 위치에 대하여, 앞의 원소의 값을 비교하며 값을 갱신한다.
for (int j = 0; j < i; j++) {
// 만일 부분 수열이 증가할 가능성이 있다면
if (arr[j] < arr[i]) {
// dp 테이블에 저장된 LIS를 바탕으로 가장 큰 값을 dp[i]의 값으로 갱신한다.
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
// 전체 수열에서 LIS의 값을 갱신한다.
max = Math.max(max, dp[i]);
}
System.out.println(max);
sc.close();
}
}
2️⃣ 이분 탐색을 활용한 풀이
public class LIS2 {
static int N;
static int[] arr, dp;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
arr = new int[N];
for (int i = 0; i < N; i++) {
arr[i] = sc.nextInt();
}
// dp에 실질적으로 저장된 원소의 길이 = LIS인 1차원 dp테이블을 만든다.
// 해당 dp에 저장된 원소(0이 아닌 값)들은 이후 조사하는 원소들이 부분 수열을 늘릴 수 있을지에 대한 정보를 제공한다.
dp = new int[N];
// 처음에 저장된 원소는 없으므로, LIS = 0이다.
int LIS = 0;
// 첫 번째 원소부터 N번째 원소까지 dp 테이블의 값을 채워 나간다.
for (int i = 0; i < N; i++) {
// 이분 탐색을 활용하여 dp테이블에 저장된 원소를 탐색하며 현재 선택된 숫자가 dp테이블의 어떤 위치에 포함될지 파악한다.
int idx = BinarySearch(arr[i], 0, LIS, LIS + 1);
// 찾지 못한 경우
if(idx == -1) {
// 가장 마지막 위치에 원소를 삽입하고 LIS의 길이를 늘린다.
dp[LIS++] = arr[i];
}
// 찾은 경우
else {
// 해당 위치에 현재 값을 삽입하여 갱신한다.
dp[idx] = arr[i];
}
}
// LIS의 길이를 출력한다.
System.out.println(LIS);
sc.close();
}
private static int BinarySearch(int num, int start, int end, int size) {
int res = 0;
while (start <= end) {
// 중앙 값을 찾는다.
int mid = (start + end) / 2;
// 만일 현재 선택된 원소가 해당 원소보다 작거나 같다면, 앞 부분을 탐색한다.
if (num <= dp[mid]) {
// 해당 원소의 위치를 기억해둔다.
res = mid;
end = mid - 1;
}
// 만일 현재 선택된 원소가 해당 원소보다 크다면, 뒷 부분을 탐색한다.
else {
start = mid + 1;
}
}
// dp테이블에서 삽입될 위치를 찾지 못한 경우(즉, 모든 수들보다 큰 경우).
if (start == size) {
return -1;
}
// dp테이블에서 삽입될 위치를 찾은 경우.
else {
return res;
}
}
}
🔍 최장 증가 부분 수열 문제
백준 11053 가장 긴 증가하는 부분 수열 - S2
https://www.acmicpc.net/problem/11053
11053번: 가장 긴 증가하는 부분 수열
수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이
www.acmicpc.net
백준 11054 가장 긴 바이토닉 부분 수열 - G4
https://www.acmicpc.net/problem/11054
11054번: 가장 긴 바이토닉 부분 수열
첫째 줄에 수열 A의 크기 N이 주어지고, 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ N ≤ 1,000, 1 ≤ Ai ≤ 1,000)
www.acmicpc.net
백준 2631 줄세우기 - G4
https://www.acmicpc.net/problem/2631
2631번: 줄세우기
KOI 어린이집에는 N명의 아이들이 있다. 오늘은 소풍을 가는 날이다. 선생님은 1번부터 N번까지 번호가 적혀있는 번호표를 아이들의 가슴에 붙여주었다. 선생님은 아이들을 효과적으로 보호하기
www.acmicpc.net
'CS > 알고리즘' 카테고리의 다른 글
누적합 (Prefix Sum) (0) | 2023.05.24 |
---|---|
유니온 파인드 (Union-Find) (0) | 2023.04.13 |