Even Idiots Can Make Game
백준 17298 G4 오큰수
- Date/Lastmod
- Section
- PS
- Title
- 백준 17298 G4 오큰수
- Ps-Tags
- Solved
- 03-05/25
#1 브루트 포스로?
수열의 길이 $N$이 $[1, 10^6]$ 범위이고, 시간 제한이 1초이다.
“오큰수” 라는 개념 자체는 단순해서, 처음에는 완전 탐색으로 배열을 순회하면서, 순회하는 요소의 오른쪽 범위부터 끝까지, 처음으로 발견된 그 요소보다 작은 요소를 직접 찾아보았다.
|
|
이러면 사실상 요소별로 $N$만큼 순회하는 것이므로 복잡도는 $O(N^2)$이고, $N$의 범위를 고려했을 때 문제에서 주어진 시간 제한인 1초를 당연히 벗어난다.
#1 그럼 어떻게? - 스택 사용
예를 들어 수열 {3, 2, 7}
을 다음 처럼 순회한다고 생각하자.
3
이 순회 요소일 때, 아직 오큰수를 모른다.3
은, “오큰수를 모른다” 고 마킹해둔다.2
가 순회 요소일 때:
2
는3
보다 작으므로, 가장 최근에 “오큰수를 모른다” 고 마킹해 둔3
의 오큰수는2
가 될 수 없다.2
는 아직 오큰수를 모른다.2
를, “오큰수를 모른다” 고 마킹해둔다.7
이 순회 요소일 때:
7
는2
보다 크므로:
- 가장 최근에 “오큰수를 모른다” 고 마킹해 둔
2
의 오큰수는7
이다.- 그 바로 이전에 “오큰수를 모른다” 고 마킹해 둔
3
또한7
보다 작으므로,3
의 오큰수도7
이다.- 이제 더이상 “오큰수를 모른다” 고 마킹해 둔 요소는 없다.
7
은 아직 오큰수를 모른다. 그런데 더이상 순회할 것이 없다.
그러면 {3, 2, 7}
오큰수 배열은 {7, 7, -1}
이 된다.
로직을 보면, “오큰수를 모든다” 라는 정보를 저장해야 하는데, 이 정보를 저장하고 꺼내는 순이 반대 임을 알 수 있다. 가장 나중에 넣은 것이 가장 먼저 사용되는 후입선출(LIFO) 구조이다. 때문에 이 메모리는 스택으로 관리해야 한다.
이 방법을 사용하면 한 번의 순회로 모든 오큰수를 찾아낼 수 있지만, 순회하는 시점에 즉시 오큰수를 찾아내는 것이 아니므로 수열의 인덱스에 해당하는 오큰수를 저장하는 배열이 하나 더 필요하다. 이 배열은 처음에는 모두 오큰수를 발견하지 못한 상태이므로, -1
로 초기화 해두면 된다.
#1 코드
코드는 아래와 같다.
|
|
#1 기타
#2
std::find
와 std::find_if
이름에 속지 말고 find
계열 함수는 모두 조건을 만족하는 요소가 첫 번째로 발견된 위치를 반환함. 1
std::find
operator==
가 정의된 타입에서만 사용 가능operator==
만 정의하면 사용자 정의 자료형에서도 사용 가능- 비교 함수를 전달할 수 없음.
std:find_if
, std::find_if_not
- 비교 함수를 직접 전달하여 사용
std::find_if_not
은 비교 함수가 거꾸로 적용됨
#2 컨테이너가 비어 있을 때 멤버 접근
컨테이너가 비어 있을 때, 멤버 접근 메서드를 호출하는 경우에 대한 정리이다.
이런 기믹(?)들은 계속 써먹으면서 기억하는 수밖에 없다. 비어있는지 아닌지는 항상 조심하자.
시퀀스 컨테이너
front
,back
,[]
는 정의되지 않은 행동at
만큼은 예외(std::out_of_range
) 발생
스택, (우선순위)큐
top
,front
,back
역시 정의되지 않은 행동
연관 컨테이너
[]
는 특정 키가 없으면 새로운 키를 생성함at
은 특정 키가 없으면 예외(std::out_of_range
)를 발생시킴
컨테이너가 비어있을 때, begin
, end
등을 사용하여도 완전 정상이나, begin
을 역참조 하는 것은 정의되지 않은 행동.
왜 헷갈렸냐면, STL 함수 중에
std::find_first_of
가 있거든. ↩︎