<

PS/알고리즘

코딩테스트를 효율적으로 대비하는 방법에 대하여

leedongbin 2024. 2. 11. 20:46
이 글은 지극히 주관적인 내용이므로, 반박은 언제나 환영합니다!
비슷한 주제의 좋은 글이 있으니 같이 읽어보셔도 좋겠습니다.

슬슬 취업 시기가 다가오는 나이가 되어, 요즘 친구들에게 코딩테스트를 어떻게 준비해야 하는지 질문을 자주 받곤 합니다. 그런데 다들 궁금해하고 어려워하는 내용이 비슷해서, 이참에 구체적으로 생각을 정리해봤습니다. 많은 분에게 도움이 되었으면 좋겠습니다.

답변에 대한 제 나름의 이유도 적어두었으니, 여러분께 공감되는 답변만 참고하시면 좋을 것 같습니다.


1. 문제를 풀다 도저히 모르겠으면, 풀이를 보는 게 좋을까요? 아니면 끝까지 고민하는 게 좋을까요?

- 풀이를 보는 게 좋다고 생각합니다.

우선 두 방식의 장단점을 알아봅시다.
풀이를 보면서 공부하면, 다양한 문제를 경험할 수 있지만 스스로 고민하는 능력이 다소 약해질 수 있고, 답을 봤다는 찝찝함이 남으면서 자신의 실력에 의구심이 들 수 있습니다.
반면에 끝까지 고민하면서 공부하면 사고력에 도움이 될 수 있지만, 고민하는 시간이 너무 길어지면 스트레스를 많이 받거나 성장 속도가 남들보다 느려져서 의욕이 꺾일 수 있습니다.

저는 코딩테스트 대비를 막 시작하신 분들에게 가장 중요한 두 가지가 동기부여경험치라고 생각합니다.
전자는 일단 눈에 보이는 결과물(티어)이 있기 때문에 형식적으로라도 동기부여가 되지만, 후자는 그렇지 않습니다. 그리고 다음 문항에서 자세히 다루겠지만, 너무 오래 고민하는 것은 시간제한이 있는 코딩테스트의 특성상 오히려 악효과가 될 수도 있습니다.
그리고 한번 알아두면 코테에 가끔 나오는 아이디어(ex: 백준13305, 백준1915, 백준25827 등등..)들이 꽤 있으니 양치기로 경험을 쌓는 것도 중요합니다. 이런 아이디어는 처음 마주하면 떠올리는 데 오래 걸리는 경우가 많거든요.

그러면 이제 첫 번째 방식의 단점을 해결해야 하는데...
우리의 고민은 '일단 답을 보고 풀긴 했는데, 나중에 똑같은 유형이 코테에 나와도 못 풀면 어떡하지?' 입니다.
이것은 두 가지 방법으로 어느 정도 커버할 수 있습니다.

첫번째 방법은, 정답 코드가 아닌 풀이만 보고 코드는 직접 작성해보는 것입니다.
풀이를 아는 것과 맞았습니다!!를 받는 것은 전혀 다릅니다. 실제로 저는 Open Contest나 Atcoder 등에 참가할 때 3번에 1번꼴로 풀이는 거의 아는데 코드를 시간 내에 못 짜서 문제를 못 풀곤 합니다.(이건 저한테도 좀 문제가 있는 것 같지만...)
아무튼 코드를 직접 작성해보면 구현 연습도 되고 기억에 더 오래 남게 되며, 내가 진짜로 풀이를 이해했는지 검증해볼 수 있습니다. 만약 풀이를 보고도 구현이 안 된다면 이해했다는 생각은 착각이었던 것이죠.

두번째 방법은 비슷한 유형이나 해당 아이디어의 응용문제들을 답을 안 보고 풀어보는 겁니다.
몇 가지 예시를 뽑아보자면, 이런 것들이 있겠네요.

결국 응용문제만 스스로 풀 줄 알면 풀이를 완전히 이해했다는 뜻이니, 고민과 찝찝함을 동시에 해결할 수 있습니다!
그런데 응용문제를 못 풀었다고 두 배로 좌절할 필요도 없습니다. 말 그대로 응용문제니까 거기서 새로운 아이디어를 또 얻어가면 되고, 아직 우리는 실전이 아니라 연습 중이니까요.

비슷한 문제를 찾는 게 어려울 수도 있는데, 코딩테스트를 대비하는 카톡 오픈채팅방이나 문제를 많이 풀어본 멘토에게서 정보를 얻을 수도 있고, 이런 식으로 solved.ac 검색기능을 활용해서 원하는 조건으로 문제들을 골라서 푸실 수 있습니다.


2-1. 풀이를 본다면, 얼마나 고민한 후에 보는 게 좋을까요?

- 결론부터 말하면, 대회 준비나 취미가 아니라 코딩테스트를 목표로 하는 분들이라면 아래와 같이 문제당 약 1시간 정도 사용하는 게 적당한 것 같습니다.

  1. 아무 힌트 없이 20분 고민하기
  2. 알고리즘 분류만 확인하고 10분 더 고민하기
  3. 풀이를 다 읽고, 30분 동안 직접 구현해보기
  4. 구현이 30분 안에 안 끝났다면 정답 코드까지 찾아보기.

고민하는 시간이 너무 짧다고 느껴지실 수 있는데, 이건 개인적인 취향 차이도 있고 어차피 하다 보면 필연적으로 오기가 생겨서 계획보다 더 많은 시간을 쓰게 되는 게 국룰입니다.

조금 더 구체적으로 말하자면, 자기가 준비하고 있는 시험에서 각 문제에 할당할 수 있을 시간 정도로만 고민하는 게 좋다고 생각합니다.

예를 들어 3시간에 6문제가 주어지는 코딩테스트에서 평균 4솔이 합격 컷이라면, 쉬운 문제에는 30분 정도, 어려운 문제에는 1시간 정도씩 할당할 수 있으니 그 정도만 고민해보는 거죠. 마음아픈 사실이지만, 실제로 오랜 시간을 써서 그 문제를 풀었다 하더라도 코딩테스트에서 '주어진 시간 안에' 못풀었다는 것은 변함없으니까요ㅠㅠ.

하지만 4~5시간, 3인 1팀으로 진행되는 대회 같은 경우는, 한 문제를 잡고 오래 고민할 시간이 충분하므로 연습할 때 2~3시간 이상 고민해봐도 좋을 것 같습니다.

당연히 이건 공부 목적이 시험 대비인 경우에만 그렇고, 지적 호기심을 위해 공부중이라면 더 깊게 고민하는 것도 재밌습니다! 뭐가 됐든, 마음이 꺾이지 않는 선에서 시간은 적당히 조율하는 게 좋은 것 같아요. 너무 풀이에만 의존해도, 안 풀리는 문제를 너무 오래 붙잡고 있어도 재미없어지니까요.


2-2. 저는 풀이를 보기 싫어요. 힌트 정도만 보고 싶은데, 힌트를 어디서 찾을 수 있을까요?

- 일단 문제 풀이의 일부분만 보는 것도 힌트라고 할 수 있겠지만, 이것조차 싫다면 다른 방법을 찾아봅시다.

먼저, 문제의 시간제한과 입력의 크기를 보고 시간복잡도를 유추해서 힌트를 얻는 방법이 있습니다.
보통 1초에 1~2억 번 정도 연산이 가능하다고 보는데, 예시로 시간제한 1초, $N$=10만인 문제에서는 $O(N^2)$의 풀이는 불가능하며, $N$=1,000만인 문제에서는 $O(NlogN)$의 풀이도 불가능하므로 이분 탐색, 우선순위 큐, 정렬 등을 사용하는 풀이는 배제할 수 있는 식이죠.

이 힌트는 코딩테스트 도중에도 얻을 수 있는 정보이므로, 반드시 익혀둬야할 방법입니다. 

그렇다면 시험 도중에는 얻을 수 없는 힌트에는 어떤 것들이 있을까요?
대표적으로, 알고리즘 분류를 보는 것이 풀이의 방향성을 잡아주기 때문에 큰 힌트가 될 수 있습니다.

그런데, 사실 위의 모든 내용이 힌트가 될 수 있습니다(?!). 예를 들어 이런 식이죠.

  1. 난이도 : 티어가 낮은 문제일수록, 사용되는 알고리즘의 다양성이 적어지므로 생각을 좁힐 수 있습니다. (≒ 알고리즘 분류를 보는 것)
  2. 채점결과 : 타인의 채점결과가 시간 초과, 런타임 에러(DivisionByZero, IntegerOverflow)등 특정 유형이 많다면, 내 실수를 점검하는 데 참고할 수 있습니다. (반례를 보는 것)
  3. 메모리, 시간 : 타인의 정답 코드와 내 코드의 예상 시/공간복잡도를 비교하면서 내가 올바른 접근을 하고 있는 지 판단해볼 수 있습니다. ( 알고리즘 분류를 보는 것)
  4. 제출 언어 : 자주 있는 일은 아니지만, 특정 언어로만 많이 풀린 경우 그 언어에 유리한 풀이가 존재할 가능성이 있고, Text 언어로 풀린 경우 입력에 상관없는 정답이 존재함을 알 수 있습니다. (ex: 백준13277, 백준15311)
  5. 코드 길이 : 풀이가 단순한 식 하나로 정리될 수 있는지, 아니면 구현이 복잡하거나 많은 예외처리가 필요한 문제인지, 때에 따라서는 런타임 전의 전처리가 필요한 문제인지 파악할 수도 있습니다.

그래서 실제로 이런 정보들을 얻을 수 없는 대회에서는 보통 스코어보드에 풀린 문제들을 따라 풀기 때문에 의외로 쉬운 문제가 덜 풀리는 사례도 많습니다.

연습도 실전처럼 아무 힌트도 없이 공부하고 싶은 분들도 계시겠지만, 저는 개인적으로 난이도 정도는 알고 푸는 게 좋다고 생각합니다. 고티어에는 사전지식 없이는 절대 풀 수 없는 문제들도 많고, 저티어에는 단순히 손가락 운동에 지나지 않는 쉬운 문제도 있을 것이기 때문에 이런 것들에 시간을 낭비할 필요는 없다고 생각합니다.

 

또다른 힌트로는 반례가 있습니다. 내 코드에 어떤 논리적 문제가 있는 지 직접 확인하는 방법입니다. 그런데 다른 것들보다도 반례를 보는 것은 특히 조심해야 합니다.

뭔가 사람 심리상, 풀이를 보는 것은 자존심이 좀 상하지만 반례를 보는 것은 좀 거부감이 덜한 느낌이 있는 것 같습니다. (저도 그렇고요.) 그래서 질문 게시판을 보면, "제 풀이가 논리적으로 잘못된 부분이 있나요?" 등의 질문보다도 "게시판의 반례를 모두 넣어봐도 다 맞아요. 다른 반례는 어떤 게 있을까요?" 같은 질문을 많이 봤던 것 같습니다.

그런데, 반례를 찾기 위해 삽질하는 시간 역시도 코딩테스트에 포함되는 시간임을 잊어선 안 됩니다. 시험시간에 압박을 느끼다 보면, 코드를 갈아엎고 다시 쓰기보다는 기존 코드에 몇 가지 예외처리를 추가해서 맞혀야겠다는 생각에 사로잡히기 쉽습니다. 그런데 오랜 고민 끝에 반례를 찾았는데, 결국 내 접근이 애초부터 틀렸다는 사실을 깨닫는다면? 고민한 시간+코드 작성한 시간+반례 찾는 시간을 날리고 태초마을로 돌아갔습니다. 시험이 말릴 수밖에 없겠죠.

그래서 결론은, 반례를 찾는 능력도 본인 실력에 포함되며, 애초에 틀린 접근을 하지 않기 위해서(최대한 반례가 없는 코드를 작성하기 위해서) 시간복잡도 분석이 중요하다는 것입니다.


3. 어떤 난이도의 문제를 하루에 몇 문제씩(몇 시간씩) 푸는 게 좋을까요?

- 이건 너무 사바사라서 제 경우를 말씀드리자면...

  • 실력 올리기 : 본인과 같은 티어의 문제
  • 내실 다지기 : 본인의 아래 티어의 문제
  • 풀어도 크게 도움 안됨 : 본인의 두 단계 이상 아래 티어의 문제

정도였던 것 같습니다.

예를 들어 본인 티어가 골드라면, 실력을 올리고 싶을 땐(새로운 자료구조나 알고리즘을 배우고 싶다면) 골드 문제를, 실력 체크용으로 실버 문제를 아무 힌트 없이 풀어보면 좋고, 브론즈 문제는 쉽게 풀 수 있는 정도면 좋은 것 같습니다. 사실 티어가 실력이랑 정비례하는 지표도 아니고, 실력은 개인차가 너무 커서 크게 의미 있는 기준도 아니니 적당히 참고 정도만 해주세요.

 

하루에 몇 문제를 풀지는 각자의 시간과 의지력에 따라 다릅니다.

저는 새싹10단계 뱃지가 갖고 싶어서 스트릭 채우기를 2년 넘게 하고 있습니다. 이제는 아까워서 억지로라도 하고 있지만... 사실 너무 오래되다 보니 PS가 숙제처럼 느껴져서 브론즈 문제로 스트릭만 채우고 넘기는 기간도 꽤 있었습니다.
그래서 이번 방학 동안은 강제성을 이용해서 1일 1플래 풀기를 하고 있는데, 힘들지만 어떻게든 하게 되니 장단점은 있는 것 같아요. 저처럼 뱃지에서 동기부여를 얻으실 분들은 스트릭에 도전해보셔도 좋을 것 같습니다!


4. 이것 외에도 추가로 하면 좋을 것들이 있을까요?

- 저는 다른 분들의 정답 코드를 보는 게 실력 상승에 도움이 많이 됐던 것 같습니다.

내 코드보다 빠르게 동작하는 코드를 보면 여러 가지 최적화 기법이나 새로운 풀이를 얻어갈 수도 있고,
내 코드보다 짧은 코드를 보면 불필요한 구현이나 예외처리를 했었는지 돌아볼 수 있습니다.

저는 이외에도 몰랐던 자료구조(pbds)나 내장함수(max_element(), builtin함수 등), 코드를 간결하게 다듬는 법 등을 배울 수 있었습니다.

  

그리고 실전과 비슷한 환경에서 코딩테스트 연습을 해보시는 것을 추천드립니다.

평소에 사용하던 IDE를 사용하지 못하는 경우는 흔하고, 간혹 사용하는 언어가 제한될 수도 있으며 데스크톱이 아닌 노트북으로 시험을 봐야 하는 등 여러 가지 변수가 생길 수 있으니 미리 대비해보는 겁니다.
저는 시간제한이 걸리면 무의식중에 긴장이 되어 딴생각을 엄청나게 하는 편인데, 이런 것도 미리 경험해보시는 게 좋아요.
백준 Open Contest나 Codeforces로 연습하시거나, 시간이 없다면 프로그래머스에서 기출문제를 시간제한을 두고 풀어보시는 게 제일 효과적일 것 같습니다. 


끝까지 읽어주셔서 감사드리고, 여러분의 의견을 자유롭게 적어주세요! (이런 내용은 특히 공감된다, 이런 내용이 추가되면 좋겠다, 이 방법은 이런 이유에서 별로인 것 같다, 실제로 이렇게 해보니 어떻더라 등등... 어떤 의견이라도 환영합니다!)

감사합니다.