호우동의 개발일지

Today :


문제 이해 단계

수열이 주어졌을 때 위치를 가만히 둔 채로,
오름차순으로 증가하는 부분 수열은 구한다.

그때 가장 크게 증가하는 부분수열의 길이를 구하는 문제

 


문제 접근 단계

문제에 나와있는 예시로 생각해 보자

10 20 10 30 20 50

1번째 수(10)에서 가장 긴 부분수열의 길이는 1이다.
2번째 수까지의 경우의 수는 3가지이다.


1. 10 - 20 (2가지)
2. 20 (1가지)
3. 10 (1가지)


이때 가장 긴 부분수열은 1번째인 2이다.

3번째 수까지의 경우의 수는
1. 10 - 20 (2가지)
2. 20 - 10 (2가지)
3. 20
...


로 2번째보다 길어지려면 20보다 수가 높아야 하는데,
그렇지 못하므로 그대로 최대 길이는 2이다.

4번째 수까지의 경우의 수는
1. 10 - 20 - 30 (3가지)
2. 10 - 30 (2가지)
3. 20 - 30 (3가지)
...
로 1번째인 3이 가장 긴 길이이다.

여기까지 규칙을 보면 4번째 수 30은
자기보다 작은 수들의 부분수열 중 가장 길이가 긴 것에 +1을 한 것이다.


여기서 보면 10 - 20 - 30은
3번째까지 길이가 가장 길었던 경우의 수에 30을 붙인 것일 뿐이다.

이것을 우리는 일반화할 수 있다.

x까지의 부분수열의 최대길이는
x보다 작은 수들 중의 부분수열의 최대길이 + 1이다.

이것을 DP로 환원하여 계산식을 세워보면
DP [x] = MAX(DP [1~x-1])라고 할  수 있다.


자세한 것은 구현은 코드를 통해 설명하겠다.

 

 


문제 구현 단계

#include <iostream>

using namespace std;

int N;
int d[1001];
int v[1001];
int main() {
	cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);

	cin >> N;
	for (int i = 1; i <= N; i++) cin >> v[i];

	int ans = 1;

	for (int i = 1; i <= N; i++) {
		d[i] = 1;
		for (int j = 1; j < i; j++) {
			if (v[i] > v[j]) {
				d[i] = max(d[i], d[j] + 1);
				ans = max(ans, d[i]);
			}
		}
	}
	cout << ans;
}

이중 반복문을 통해 DP를 구현하였다.


첫 번째 반복문에서 기준이 될 x를 정하고
두 번째 반복문에서 1~x-1까지를 반복해 max를 구한다.

j의 값이 i값보다 작을 때만 비교를 해주고 이것이 ans보다 클 때만 갱신해 준다.


이중 반복문으로 해당 문제를 풀 수 있는 이유는
N = 1000이기 때문에 1000 * 1000 = 10^6으로 1초가 넘어가지 않기 때문에 가능했다.

 

 


시행착오

굉장히 푸는데 난해하고 어렵던 문제였다.


3일 내내 고민하다가 방식을 여러 번 바꿔보고 해 봤는데도
전혀 안 풀려서 결국에는 인터넷을 참고할 수밖에 없었다.

DP를 항상 재귀함수로만 접근했었는데 이렇게 반복문으로도 접근할 수 있구나..
그리고 이렇게 내가 생각의 폭이 짧았구나.

잘못된 생각의 늪에 빠지니까 헤어날 수가 없어서
만약 아직까지 잡고 있었다면 여전히 못 풀었을 것 같다.

ㅠㅠ