Problem Link
Description of Problem
자동차의 행렬이 주어진다. 행렬에는 자동차의 무게가 저장된어 있다. 이때 자동차를 연결한다. 자동차를 연결하는 제약조건은 앞쪽 즉, 왼쪽의 자동차가 뒷쪽 즉, 오른쪽의 자동차보다 무거워야 된다. 이때 주어진 조건을 만족하는 가장 긴 자동차 행렬의 길이를 구하는 문제다.
Algorithm to Solve the Problem
답은 제약조건을 만족하는 가장 긴 행렬의 길이이다. 하지만 이 길이를 구하기 위하여 실제로 연결되는 자동차들의 행렬을 생각해보자. 어떤 자동차를 정답 행렬에 추가하기로 결정했다. 이때 다음으로 일어날 수 있는 모든 경우는 다음과 같다.
1. 더 가벼운 자동차라면 해당 자동차 뒤에 붙인다.
2. 더 무거운 자동차라면 해당 자동차 앞에 붙인다.
3. 자동차를 붙이지 않는다.
여기까지 생각하고 보면 문제가 매우 단순해진다. 정답 행렬에 가장 먼저추가 되는 자동차는 결과적으로 정답행렬에 중간에 위치하게 된다.(대부분의 케이스에서) 즉, 가장 먼저 추가되는 자동차를 시작점으로 마지막 끝점까지의 최대 증가 부분 수열과 최대 감소 부분 수열의 합이 가장 큰 시작점을 찾는 다면 해당 시작점이 정답 수열의 가운대에 위치하며 해당 시작점 앞으로는 최대 감소 수열이 해당 시작점 뒤로는 최대 증가 수열이 위치하는 최적해를 구할 수 있는 것이다.
Analysis Time Complexity
위 알고리즘의 시간복잡도는
O(각 시작점당 lis와 lds를 구하는 시간 + 각 시작점당 lis와 lds의 합 비교)이다.
이를 자동차수의 최댓값은 2000 이므로 시간복잡도는 아래 이하이다.
O(2000 * 2000 + 2000) = O(4000000)
4000,000은 컴퓨터가 계산하기에 너무나도 여유로운 숫자이다.
Solution Code for This Problem
#include<cstdio>
#include<algorithm>
using namespace std;
int main() {
int cars[2002], lis[200], lds[200], cases, n;
scanf("%d", &cases);
while(cases--) {
scanf("%d", &n);
for(int i = 0; i < n; ++i)
scanf("%d", &cars[i]);
//cars[i]를 시작점으로 하는 lis를 구하는 부분
for(int i = n - 1; i >= 0; --i) {
lis[i] = 1;
for(int j = i + 1; j < n; ++j)
if(cars[i] < cars[j])
lis[i] = max(lis[j] + 1, lis[i]);
}
//cars[i]를 시작점으로 하는 lds를 구하는 부분
for(int i = n - 1; i >= 0; --i) {
lds[i] = 1;
for(int j = i + 1; j < n; ++j)
if(cars[i] > cars[j])
lds[i] = max(lds[j] + 1, lds[i]);
}
int ret = 0;
for(int i = 0; i < n; ++i)
ret = max(ret, lis[i] + lds[i] - 1);
printf("%d\n", ret);
}
}
'Competitive Programming 3rd(Uva Judge)' 카테고리의 다른 글
다익스트라 알고리즘(Dijkstra), 가중치가 있는 그래프의 최단 경로 (0) | 2018.01.27 |
---|---|
최소 스패닝 트리(MST, Minimum Spanning Tree) (1) | 2018.01.26 |
절단점 및 다리(구하기) (0) | 2018.01.19 |
Fenwick Tree(Binary Indexed Tree, BIT) (0) | 2018.01.19 |
Uva 11902 - Domanitor (0) | 2018.01.18 |