일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- DP
- 패스트캠퍼스
- union find
- Lis
- 재귀
- 비트마스킹
- 백준 1464
- disjoint set
- 최장길이바이토닉수열
- 깊이 우선 탐색
- 브루트포스
- 최장증가수열
- 구현
- 결정 문제
- bfs
- 이분탐색
- 이분 탐색
- 그래프이론
- 2493 백준
- 그래프 이론
- 뒤집기 3
- 분할정복
- boj 1464
- 서로소 집합
- 1939백준
- 결정문제
- 그래프탐색
- parametric search
- 그래프 탐색
- 백준 뒤집기 3
- Today
- Total
알고리즘 문제풀이
[BOJ] 14002번 - 가장 긴 증가하는 부분 수열 4 본문
백준 14002번 - 가장 긴 증가하는 부분 수열 4
시간제한 | 메모리 제한 | 제출 | 정답 | 맞은 사람 | 정답 비율 |
---|---|---|---|---|---|
1 초 | 256 MB | 10668 | 4186 | 3187 | 40.301% |
문제
수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오.
예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이고, 길이는 4이다.
입력
첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000)이 주어진다.
둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000)
출력
첫째 줄에 수열 A의 가장 긴 증가하는 부분 수열의 길이를 출력한다.
둘째 줄에는 가장 긴 증가하는 부분 수열을 출력한다. 그러한 수열이 여러가지인 경우 아무거나 출력한다.
예제 입력 1
6
10 20 10 30 20 50
예제 출력 1
4
10 20 30 50
출처
www.acmicpc.net/problem/14002
알고리즘 분류
- 다이나믹 프로그래밍
접근 방법
LIS를 구하는 방법 중 대표적으로 ${O(N^2)}$ 의 방법과 ${O(NlogN)}$의 방법이 있다.
이 문제에서는 N의 범위가 ${{10}^3}$ 이므로 충분히 ${O(N^2)}$의 방법으로 풀 수 있다.
다음과 같이 메모이제이션 배열을 선언해보자.
DP[i] = i를 부분 증가 수열의 마지막 원소로 보았을 때, 최장 부분 증가 수열의 길이
그러면, DP[j]가 증가하는 조건은 i < j 인 i에 대해서 max(dp[i]) + 1 이 dp[j] 값이 된다는 것을 알 수 있다.
왜냐하면, 이전까지 구한 최장 부분 증가 수열의 길이 + 1이 결국에는 다음 stage에서도 최장 부분 증가 수열의 길이가 되기 떄문이다.
최종적으로 DP[k] ( 0 <= k < N ) 의 최댓값이 증가 수열의 최장 길이가 된다는 것을 알 수 있다.
두번째로 우리가 구해야하는 것은, 가장 긴 부분 수열을 출력하는 것인데 이것은 backtrace라는 배열을 만들어서 메모이제이션 배열이 변화할 때 어떠한 경로를 거쳐서 왔는지 기록하는 것으로 구현이 가능하다.
backtrace배열이 변화하는 경우는 메모이제이션 배열의 값이 변경될 때이다.
이전에 찾았던 (최장 부분 증가 수열 + 현재 숫자) 가 현재 backtrace배열의 값이 된다.
이러한 방식으로 모든 0 <= i < N 인 i에 대하여 backtrace 와 dp 를 초기화 시켜 준다면 여기서 dp[k]가 최댓값이 되는 k에 대해서 dp[k]와 backtrace[k]를 출력하면 우리가 원하는 "가장 긴 증가하는 부분 수열의 길이" 와 "가능한 경로 중 하나"를 출력할 수 있다.
소스 코드
#include <bits/stdc++.h>
using namespace std;
int main(void){
ios_base::sync_with_stdio(false);
cin.tie(0);
int N;
cin >> N;
vector<int> arr(N);
for(int i=0; i<N; i++){
cin >> arr[i];
}
// dp[i] : i까지 검사했을 때 부분 증가 수열의 길이
vector<int> dp(N, 1);
// 현재까지 밟고 온 것들의 경로
vector<int> backtrace[N];
for(int i=0; i<N; i++){
backtrace[i].push_back(arr[i]);
}
for(int i=1; i<N; i++){
for(int j=0; j<i; j++){
if( arr[j] < arr[i] && dp[i] < dp[j] + 1 ){
dp[i] = dp[j] + 1;
backtrace[i] = backtrace[j];
backtrace[i].push_back(arr[i]);
}
}
}
// dp[0 ~ N-1] 중 가장 큰 값이 가장 긴 증가하는 부분 수열의 길이가 됨
int max_idx = 0;
for(int i=1; i<N; i++){
if( dp[max_idx] < dp[i] ){
max_idx = i;
}
}
cout << dp[max_idx] << '\n';
for(auto elem : backtrace[max_idx])
cout << elem << ' ';
return 0;
}
'자료구조 + 알고리즘 > [BOJ]' 카테고리의 다른 글
[BOJ] 1202번 - 보석 도둑 (0) | 2021.04.27 |
---|---|
[BOJ] 1766번 - 문제집 (0) | 2021.04.26 |
[BOJ] 11049번 - 행렬 곱셈 순서 (0) | 2021.02.19 |
[BOJ] 13549번 - 숨바꼭질 3 (0) | 2021.02.19 |
[BOJ] 1082번 - 방 번호 (0) | 2021.02.10 |