[백준] [C/C++] [★] 14002번 : 가장 긴 증가하는 부분 수열 4
업데이트:
문제
수열 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
알고리즘 [ 접근 방법 ]
DP를 사용해 푸는 LIS (Longest Increasing Subsequence)문제이다.
이전에 문제를 점화식으로 바로 바꿔서 풀던 문제와는 직접 일일히 비교를 한뒤 점화식으로 나타내 풀어낼 수 있다.
D[i] = A[1], … , A[i] 까지 수열이 있을 때, A[i]를 마지막으로 하는 가장 긴 부분 수열의 길이 D[i]는 A[i]가 반드시 포함되어야만 한다.
이때 A[?], A[?], … , A[j]는 D[j]라고 나타낼 수 있고 아래 그림같은 과정을 보인다.
이때 D[i] = max ( D[j] ) + 1 , (단, j<i 이고 A[j] < A[i]) 라고 정의 할 수 있다.
★★★여기서 점화식을 세울때 반드시 기억해야할 것이 하나있다!!! 점화식에 또 다른 변수가 들어간다면 반드시 변수의 범위를 지정해줄 필요가 있다!!!
여기까지가 LIS의 길이를 구하는 과정이고, 문제에서는 LIS인 수열까지 출력하라는 문제이므로 추가 조건이 필요하다.
[ 역추적 ] 하나의 값이 다른 하나의 값에 의해서 변화 될 때, 어떤 값에 의해서 변화하는지를 저장해두는 과정을 말하고 보통 재귀함수를 이용해 구현한다.
여기서 D[i]의 값은 어떤 값에 의해서 결정되는지를 역추적 해보면 된다. D[6] = 4인 이유는 i=4일때 D[4]=3이므로 +1을 해서 4가 된것이다. 따라서 D[6]을 결정지은 값은 i=4 이고 이를 새로운 배열 v에 넣어준다.
같은 과정으로 D[5]= 2인 이유는 i=3일때에 의해 결정된 것이므로 i=3을 v에 넣어준다.
즉, V[i] = A[i]의 앞에 와야하는 수의 인덱스 를 의미한다. 이 과정을 역추적이라고 하고 굉장히 많이 사용되는 방법이므로 반드시 알아둬야 한다.
이를 코드로 구현하면 다음과 같다.
// 역추적 재귀함수
void go(int p) {
// ? -> ? -> ... -> a[v[p]] -> a[p]
if (p == -1) return;
go(v[p]);
cout << a[p] << " ";
}
풀이
#include <iostream>
using namespace std;
int d[1001];
int a[1001];
int v[1001];
// 역추적 재귀함수
void go(int p) {
// ? -> ? -> ... -> a[v[p]] -> a[p]
if (p == -1) return; // v[i] 를 -1로 초기화했기때문에
go(v[p]);
cout << a[p] << " ";
}
int main() {
int num;
cin >> num;
for (int i = 0; i < num; i++) cin >> a[i];
for (int i = 0; i < num; i++) {
d[i] = 1;
v[i] = -1;
for (int j = 0; j < i; j++) {
if (a[j] < a[i] && d[i] < d[j] + 1) {
d[i] = d[j] + 1;
v[i] = j;
}
}
}
int max = d[0];
int p = 0;
for (int i = 0; i < num; i++) {
if (max < d[i]) {
max = d[i];
p = i;
}
}
cout << max << endl;
go(p);
}
댓글남기기