자 오늘을 마지막으로... 하려고 했는데 생각보다 길어져서 다시 나누게 되었습니다.

생각 날때마다, 여러가지 잡기술들을 모아서 알려드리도록 할께요.

오늘은 python에서 기본적으로 제공해주는 정렬을 활용해서 조금이라도 코드를 편하고, 가독성있게 (나에게만 일지도...) 짤 수 있는 비법들을 알려드리도록 하겠습니다.

오늘 배울 내용들을 간단히 나열해드리자면,

  • sort, sorted 함수
  • enumerate 함수
  • for문에서 이름으로 원소 꺼내오기
  • 이들을 활용한 정렬처리 잡기 술

입니다.

정렬 트릭

정렬은 문제를 풀때 굉장히 자주 사용하게 되는 라이브러리입니다.

단순히 오름차순으로 정렬할 때 python에서 제공하는 sorted 라이브러리나 sort라이브러리를 사용합니다. 간단한 정렬 문제를 해보도록 할께요.

정렬 기초

이번에 풀어볼 문제는 수 정렬하기 입니다.

https://www.acmicpc.net/problem/2750

입력

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 숫자가 주어진다. 이 수는 절댓값이 1,000보다 작거나 같은 정수이다. 수는 중복되지 않는다.

출력

첫째 줄부터 N개의 줄에 오름차순으로 정렬한 결과를 한 줄에 하나씩 출력한다.

예제 입력

5
5
2
3
4
1

예제 출력

1
2
3
4
5

문제가 간단해서 좋군요. 뭐가 문제겠어요? 정렬을 알아보는 가벼운 예제니까 빠르게 후딱처리하고 넘어갑시다.

사실 이 문제는 2가지 해법이 있어요. 크게 어려운 해법은 아니고, 원본 배열을 건드리냐 안건드리냐에 따라서 달라지게 되죠. 일단 먼저 보실까요?

답 코드1 - sorted

N = int(input())
D = [int(input()) for i in range(N)]
sorted_D = sorted(D)
for num in sorted_D:
    print(num)

답 코드2 - sort

N = int(input())
D = [int(input()) for i in range(N)]
D.sort()
for num in D:
    print(num)

sorted는 D를 건드리지 않고 새롭게 정렬된 배열을 return합니다.

그에 비해서 sort는 D의 배열 자체를 정렬된 상태로 바꿔버리죠.

이정도면 기본은 되었습니다. 이제 트릭을 알아보러 가시죠.

복잡한 정렬 수행하기

기본적으로 제공되는 함수는 여러가지 조건으로 복잡한 정렬을 수행하기위해서는 비교하기 위한 함수를 넘겨줘야하는 불편한 점이 있죠.

특히 많은 문제에서 여러가지 조건에 따라 비교를 수행해야하는 경우가 왕왕있습니다.

알고리즘은 정확하게 짜는 것도 중요하지만, 빠르게 짜는게 중요한 역할을 합니다. 그때 복잡한 비교구문을 일일이 작성해서 정렬하는 것은 시간낭비입니다.

빠르게 정렬하기 위한 꿀팁을 알려드릴께요. 바로 python 내장 정렬을 이용하는 방법입니다.

python 내장정렬은 다음과 같은 특징을 가지고 있습니다.

  1. 정렬은 오름차순으로 수행한다. (위의 예제에서 보여드렸죠?)
  2. 만약 정렬의 내용물이 tuple이라면 tuple의 낮은 인덱스에 있는 원소를 기준으로 정렬한다.

2번 항목이 잘 이해가지 않으실꺼에요. 간단한 예제를 드릴께요.

a = [
    (2, 4),
    (2, 3),
    (1, 4),
    (1, 3),
]
a.sort()

print(a)

이 코드의 출력은 무엇일까요? 바로

[(1, 3), (1, 4), (2, 3), (2, 4)]

가 됩니다.

첫번 째 원소 1과 2를 비교해서 오름차순으로 정렬을 수행하죠.

만약 첫번째 원소가 같다면 두번째 원소 3과 4를 비교해서 오름차순으로 정렬을 수행합니다.

직관적으로 당연히 그렇게 되어야 맞겠죠?

바로 이 트릭을 이용해서 복잡한 정렬을 추가적인 함수를 작성하지 않고 수행할 수 있습니다.

바로 문제로 가실께요.

https://www.acmicpc.net/problem/1181

문제

알파벳 소문자로 이루어진 N개의 단어가 들어오면 아래와 같은 조건에 따라 정렬하는 프로그램을 작성하시오.

  1. 길이가 짧은 것부터
  2. 길이가 같으면 사전 순으로

입력

첫째 줄에 단어의 개수 N이 주어진다. (1 ≤ N ≤ 20,000) 둘째 줄부터 N개의 줄에 걸쳐 알파벳 소문자로 이루어진 단어가 한 줄에 하나씩 주어진다. 주어지는 문자열의 길이는 50을 넘지 않는다.

출력

조건에 따라 정렬하여 단어들을 출력한다. 단, 같은 단어가 여러 번 입력된 경우에는 한 번씩만 출력한다.

예제 입력

13
but
i
wont
hesitate
no
more
no
more
it
cannot
wait
im
yours

예제 출력

i
im
it
no
but
more
wait
wont
yours
cannot
hesitate

자 다소 복잡하죠? 하지만 코드는 문제보다 짧고, 쉽습니다.

바로 보시면서 이해하시죠.

정답 코드

N = int(input())
D = [input() for i in range(N)]
D = set(D)
D = [(len(s), s) for s in D]
D.sort()

for l, s in D:
    print(s)

1~2: 첫 두줄은 늘상 같은 인풋받는 방식입니다.

3~3: 중복을 없애주기 위해서 set 함수를 사용합니다.

4~5: 이게 바로 제가 말하고자하는 정렬트릭입니다.

python의 정렬은 첫번째 것을 먼저 정렬하고, 그다음 우선순위를 두번째 원소로 둡니다.

따라서 정렬의 첫번째 조건을 첫번째 원소에 두번째 조건을 두번째 원소에 넣는 방식으로 넣어두고 정렬을 할 수 있습니다.

만약 길이가 긴 것순으로 정렬하라고 했다면 어떻게 하면 될까요?

N = int(input())
D = [input() for i in range(N)]
D = set(D)
D = [(-len(s), s) for s in D]
D.sort()

for l, s in D:
    print(s)

라고 하시면 됩니다.

이처럼 오름차순, 내림차순도 안에 넣는 원소를 바꾸기만 해서 구현해낼 수 있습니다.

기본정렬에서 추가적인 함수를 호출하지 않고도 처리할 수있어요.

자잘한 꿀팁

사실 이보다 간단하게 처리할 수 있어요.

list뿐 아니라 set도 list처럼 입력을 받으면서 초기화할 수 있습니다.

N = int(input())
D = {input() for i in range(N)}
D = sorted([(len(s), s) for s in D])

for l, s in D:
    print(s)

당연히 더 줄일수도 있죠.

하지만 이 이상부터는 숏코딩의 영역입니다.

착한 어른이들은 따라하지 마세요!

정렬 트릭의 활용

https://www.acmicpc.net/problem/10814

눈에 보이는 조건들만이 조건이 아닙니다. 들어온 순서나, 길이, 입력받은 상수값 같은 것들도 이에 활용할 수 있습니다. 위 문제에도 이를 활용할 수 있습니다.

문제 설명은 지면이 길어져서 생략하도록 할게요.

입력 예제 1

3
21 Junkyu
21 Dohyun
20 Sunyoung

정답 코드

N = int(input())
D = [input().split(" ") for i in range(N)]
D = sorted([(int(age), order, name) for order, (age, name) in enumerate(D)])

for age, order, name in D:
    print(age, name)

이 코드에도 몇가지 보여드릴께 있습니다. 이 코드에서 배울 것은 크게 3가지입니다.

  1. enumerate
  2. for문의 unpack 트릭
  3. order를 정렬의 조건으로 넣기

enumerate

enumerate는 반복문을 돌 때, 배열의 인덱스, 배열의 원소 두가지를 활용할 수 있게 해줍니다.

3개라구요? 아닙니다 잘 보세요! 2개입니다.

order, (age, name) 2개라구요.

for문에서 이름 붙여서 가져오기

이것이 바로 python에서 for문에서 container를 unpack할 시에 제공하는 문법입니다.

아마 컨테이너 안에 있는 tuple에서 몇가지 원소를 이름을 붙여서 가져올 때 사용해보셨을 텐데, 컨테이너 안에 컨테이너에서 이름을 붙여서 가져올 수 있다는 사실은 사람들이 잘 모르시는 경우가 많았습니다.

이 경우에는 괄호를 생략하실수 없습니다! 바로 container에서 가져옴과 동시에 이름을 붙일 수 있기 때문에 코드의 가독성이 더 올라가게 되죠.

순서를 정렬의 기준으로 만들기

enumerate에서 가져온 배열의 인덱스는 곧 input을 받은 순서입니다. 문제의 조건에서

  1. 나이
  2. 들어온 순서 (가입한 순서)

라고 했기 때문에 (int(age), order) 순으로 만든 것이죠.

엇 그런데 name이 추가로 들어가 있네요?

맞습니다. 실제로 출력을 할 때에는 이름 정보가 필요합니다.

정렬시에 사용하기위해서가 아니라 그 데이터 자체를 보존하기 위해서 넣어 준거에요.

연습 문제

https://www.acmicpc.net/problem/10825

마무리

사실 오늘로 끝내려고 했는데, 정렬처리 잡기술이 조금 주저리 주저리가 길어졌네요.

다음에 시간이되면 다른 잡기술들도 소개하도록 하겠습니다.

생각보다 제가 사용하는 잡기술들이 많네요...

graph 알고리즘에서 사용하는 것들도 많고, 슬라이싱을 이용한 잡기술들도 굉장히 많은데

흠... 언제 다쓰지 이거

맥주를 먹고 그만 월요일이 되어버렸어요.

자 (1)에서 배웠던 테크닉으로 여러 형태의 입력을 받아볼거에요.

이번 시간에는 배열을 인풋으로 받는 방법에 관해서 종류별로 가볍게 설명드리고 넘어가려고 해요.

C, C++, Java 사용자들은 배열을 인풋으로 받기전에 먼저 배열은 선언해두는 것이 익숙합니다.

하지만 python의 경우 배열 선언이 매우 귀찮을 뿐아니라, 고정된 크기로 메모리를 잡아두는 것이 상당히 어렵습니다.

따라서, 그냥 input을 받으면서 동시에 input을 받은 값으로 초기화하는 코드를 자주 사용하게 됩니다.

이것도 마찬가지로 손쉽게 map으로 해결할 수 있는데, 각 예제로 설명 드릴께요.

1차원 배열을 인풋으로 받기

1차원 배열을 선언할 수 있는 문제를 먼저 하나 가져와볼께요.

X보다 작은 수라고하는 문제입니다.

https://www.acmicpc.net/problem/10871

X보다 작은 수

입력 서술

첫째 줄에 N과 X가 주어진다. (1 ≤ N, X ≤ 10,000)
둘째 줄에 수열 A를 이루는 정수 N개가 주어진다. 주어지는 정수는 모두 1보다 크거나 같고, 10,000보다 작거나 같은 정수이다.

입력 예제1

10 5
1 10 4 9 2 3 8 5 7 6

입력 받는 코드

N, X = map(int, input().split(" "))
ARR = list(map(int, input().split(" ")))

이 코드를 실행했을 때, 위의 예제1을 받으면 아래처럼 저장됩니다.

사족 - 왜 굳이 map함수 이후 list형으로 변환을?

왜 굳이 map함수를 사용한 후에 다시 list 형으로 변환을 해줬을까요?

사실 map 함수의 결과물인 map object는 iteration을 돌 수 있지만,

임의의 인덱스로 접근이 불가능합니다. map함수의 리턴형은 list가 아닙니다!

즉, [0] [1] [2] 와같은 인덱스로 접근하는 것이 불가능해요.

따라서 list 함수로 형을 변환해줘야만 접근이 가능하다는 것이죠!

아래 코드를 실행하면 에러가 발생할 거에요 한번 테스트 해보세요!

test_var = map(int, "1234")
print(test_var[1])

그렇기 때문에 map으로 생성한 것을 리스트와 같이 사용하려면 list로 캐스팅해줘야 합니다.

하지만 1번째 줄처럼 map에서 나온 오브젝트를 각 변수에 바로 나눠서 담는 경우는 굳이 map을 list로 캐스팅해주실 필요는 없습니다.

2차원 배열을 인풋으로 받기

입력 사이에 구분자가 있는 경우 - 종이의 개수

종이의 개수라는 문제로 예제를 들어볼까해요.

https://www.acmicpc.net/problem/1780

입력 서술

첫째 줄에 N(1 ≤ N ≤ 37, N은 3k 꼴)이 주어진다. 다음 N개의 줄에는 N개의 정수로 행렬이 주어진다.

입력 예제 1

9
0 0 0 1 1 1 -1 -1 -1
0 0 0 1 1 1 -1 -1 -1
0 0 0 1 1 1 -1 -1 -1
1 1 1 0 0 0 0 0 0
1 1 1 0 0 0 0 0 0
1 1 1 0 0 0 0 0 0
0 1 -1 0 1 -1 0 1 -1
0 -1 1 0 1 -1 0 1 -1
0 1 -1 1 0 -1 0 1 -1

입력 받는 코드

N = int(input())
PAPER = [list(map(int, input().split(" "))) for i in range(N)]

이 코드를 실행했을 때, 위의 입력 예제1을 받으면 아래처럼 저장됩니다.

저번 포스팅에서 한줄로 들어온 데이터를 입력 받는 법을 배웠었죠.

이를 활용하면 위와 같은 코드 한줄로 2차원 배열의 입력을 받을 수 있습니다.

구분자가 space bar이고, 이 구분자를 배열을 생성하면서 N번 동시에 받은 거죠.

다른 언어에 비해서 이런 부분은 확실히 편하게 입력을 받을 수 있는 것 같습니다.

입력 사이에 스페이스가 없는 경우 - 알고스팟

이러한 형태의 문제들이 많지는 않습니다. 하지만 분명이 존재하고 가끔 생각이 안돌아갈때 어떻게 입력을 받아야하지... 라는 고민을 하게되곤 해요.

알고스팟으로 그 예제를 들어볼까해요.

https://www.acmicpc.net/problem/1261

입력 서술

첫째 줄에 미로의 크기를 나타내는 가로 크기 M, 세로 크기 N (1 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 미로의 상태를 나타내는 숫자 0과 1이 주어진다. 0은 빈 방을 의미하고, 1은 벽을 의미한다.
(1, 1)과 (N, M)은 항상 뚫려있다.

입력 예제 1

3 3
011
111
110

자 이 문제의 입력은 기존에 배웠던 원리로 매우 간단하게 처리 할 수 있어요.

입력 받는 코드

N, M = map(int, input().split(" "))
MIRO = [list(map(int, input())) for i in range(N)]

이 코드를 실행했을 때, 위의 입력 예제 1을 받으면 아래처럼 저장됩니다!

이 코드를 보시면

MIRO = [list(map(int, input())) for i in range(N)]

이번에는 input()에 split이 없어요. str 자료형은 그 자체로 list처럼 생각하셔도 무방해요. 

사실상 "011" 이면 ["0", "1", "1"] 과 같다고 할 수 있죠.

스페이스바가 없는 입력을 분리하고 싶다면, split 없이 위와 같은 코드로 입력을 받아서 처리할 수 있어요.

배열 선언하기

거의 대부분의 언어들에서 배열을 선언하는 것에 많은 코드를 필요로 하지 않습니다.

하지만 이 나쁜 python은 배열 선언하기를 더럽게 복잡하게 만들어 뒀습니다.

numpy라도 쓸수 있게 해준다면 정말 행복할 텐데, 어림도 없습니다.

대부분의 코딩 테스트에서는 numpy를 지원하지 않습니다.

배열 선언 코드

INIT = -1
# ML러들이 좋아하는 변수명
W = row     # 행
H = column  # 렬
C = channel # 3차원!
B = batch   # 4차원! 

D1 = [INIT for j in range(W)]
D2 = [[INIT for i in range(W)] for j in range(H)]
D3 = [[[INIT for k in range(W)] for i in range(H)] for j in range(C)]
D4 = [[[[INIT for l in range(W)] for k in range(H)] for i in range(C)] for j in range(B)]

맞아요. 왕도가 없습니다... 여러분이 알고 있는 이 방법이 최적입니다.

이건 죄송할 따름이네요. 순수 파이썬으로는 이 방법말고 좋은 방법을 찾지 못했습니다.

손에 익으시면 호다닥 작성 할 수 있을 겁니다.

마무리

python은 멋진 언어입니다. 배열 빼고는요.

배열은 numpy가 없으면 정말 사용하기 귀찮기 그지없습니다.

python 4쯤에는 배열을 미리 할당할 수 있는 좋은 방법이 제안되었으면 하네요.

다음 글로 마무리를 지을 예정이에요.

다음 글에서는 python의 내장함수를 이용한 여러 잡기술들을 포스팅하고자 합니다.

라떼는 말이야... 다 C언어 부터 배웠어...

그런데 요즘은 다 python으로 문제풀고 말이야...

Python으로 문제를 풀어?

그렇습니다. 이제는 python으로도 문제를 풀수 있는 시대가 도래했습니다.

C나 C++로 문제를 풀던 많은 사람들이 python으로 문제푸는 것을 두려워합니다.

python의 입출력 방식이 C나 C++과 매우 사맛디 아니하기 때문이죠.

하지만 과연 그럴까요? 익숙해지면 C나 C++보다도 손쉽게 input을 받을 수 있습니다.

머신러닝을 전공하시던 분들은 취직시장에 나와보니 아뿔사 코딩테스트는 봐야하는데, 익숙한 언어가 python 뿐입니다.

알고리즘을 하시는 분들에게 문의를 해보니,

본디 알고리즘은 C++이 짱이다!

python이 세상에서 제일 편한 언어가 되어버렸는데, C++이라뇨...

코딩 테스트 때문에 새로운 언어를 익혀야 하는 불상사를 막고자 이번 글을 작성했습니다.

본 게시글은 맛보기인데, 제가 python으로 문제풀 때 주로 사용하는 테크닉들을 알려드리도록 하겠습니다.

제가 심심할 때 푸는 문제의 솔루션이나, 핫한 코딩테스트 문제들도 여기에 솔루션을 올리도록 할께요.

여러분들의 시간은 소중하니까.

인풋 받기

정수 하나 받기

코드

인풋을 받는 여러가지 방법들이 있습니다. 간단하게 정수 하나를 입력으로 받아 볼까요?

# "10"
N = int(input())
print(N)

이건 솔직히 새로울게 없습니다. input함수를 받고, 그 결과를 int로 캐스팅한 후, 변수 N에 넣어주는 것입니다.

input란?

input()은 입력을 받는 함수라고 알고 있습니다.

그리고 input()의 return형은 항상 str 형입니다.

그렇기 때문에, 들어온 입력을 적절히 가공해 주어야 합니다.

int()

지금으로서는 정수로 변환해주는 캐스팅 같은 역할을 한다고 생각하시면 좋을 것 같습니다.

계속 넘어가도록 하죠. 더 자세히 알 필요가 있다면 더 깊게 설명하도록 하겠습니다.

정수 두개 받기

코드

# 10 20
N, M = map(int, input().split(" "))
print(N+M) # 30

input이 한줄에 2개가 들어온다면 매우 귀찮아 집니다.

2개부터는 각각 인풋을 받아서 나누고 거기에 int로 형까지 변환해줘야합니다.

매우매우 귀찮은 일이 아닐 수 없죠.

위의 코드로 입력을 손쉽게 받을 수 있습니다.

이 코드는 아래 코드와 동일한 방식으로 작동하게 됩니다.

# 10 20
inp = input()
sp_inp = inp.split(" ")
N = int(sp_inp[0])
M = int(sp_inp[1])

int의 정체

N, M = map(int , input().split(" "))

아실지 모르겠지만 python에서 캐스팅하드시 사용하는 int, float와 같은 자료형처럼 보이는 모든 것들이 모두 함수의 형태로 사용합니다.

엄연히 말하자면 class의 형태를 가지고, int로 변환해주는 캐스팅 작업을 해줍니다.

python의 정확한 구조를 모르신다면 함수라고 생각하셔도 무방합니다! 단지 int라는 이름을 가진 함수다 라고 생각하셔도 괜찮습니다.

input 함수가 하는 일

N, M = map(int , input().split(" "))

위에서 설명했듯이 input은 입력을 받는 함수이며, 동시에 str 를 return 합니다.

그리고 더 정확히는 한 라인을 통째로 받는 함수입니다.

이 부분의 기존의 Problem Solving (PS) 계열에 있던 사람들이 고통 받는 이유중 하나입니다.

PS러들은 문자열을 스페이스 단위로 받아서 처리하고 싶어하기 때문이죠...

입력이 뭐가 들어왔던 간에 str로 한 라인을 통째로 받기 때문에 이를 필요에 따라 여러가지 형태로 가공해줘야하는 것이 매우 거슬릴 것입니다.

str의 내장 함수 split

N, M = map(int, input().split(" "))

str에는 다양한 내장 함수들이 있습니다. 이를 통해서 손쉽게 string을 가공할 수 있습니다.

ps러라면 #include <string.h> 나 #include 을 하고 고통받아 보신 분이 있을 겁니다.

python에서는 훨씬 더 쉬운 형태의 인터페이스를 제공 합니다!

pycharm같은 훌륭한 ide를 쓰신다면 "abc". 하고 컨트롤 탭을 하시면 다양한 함수들을 구경하실 수 있을 겁니다.

오늘 알아볼 함수는 split입니다.

이름부터 직관적이게 해당 문자열을 나눠주는 함수입니다.

아래 코드를 확인하시면 어떤 짓을 할 수 있는 녀석인지 직관적으로 깨달으실 수 있을 겁니다.

"10 20 30".split(" ") # ["10", "20", "30"]
"10,20,30".split(",") # ["10", "20", "30"]
"10,20,30,".split(",") # ["10", "20", "30", ""]
"10 20 30 ".split(" ") # ["10", "20", "30", ""]

다만 마지막 두 예제를 조심하세요!

마지막에도 delimiter (구분자?)가 있다면 비어 있는 string을 return할 거에요!

위와 같은 예제를 피하기 위해서는 아래와 같이 strip 함수를 사용해주면 됩니다.

"10 20 30 ".strip().split(" ") # ["10", "20", "30"]

map 함수

이제 마지막으로, map함수를 알아봅시다.

map 함수는 첫번째 인자로 함수를, 두번째 인자로 배열을 받습니다. 그래서 각 배열의 원소에 해당 함수를 적용해주는 함수입니다.

  • (사족1) 사실 함수가 아니라 callable 한 모든 것들이 여기에 올 수 있습니다.
  • (사족2) 사실 배열이 아니라 iterator를 받습니다. 대충 반복할수 있는거라고 생각하시면 될것 같습니다.

말로는 잘 이해가지 않을 것이라고 생각해요. 예제로 이해하는 편이 좋을 겁니다.

arr = ["10", "20", "30"]
map(int, arr) 
# == [int(arr[0]), int(arr[1]), int(arr[2])]
# == [10, 20, 30]

이제 https://www.acmicpc.net/problem/1000 를 풀어볼까요?

마치며

아니 벌써 마치다니요? 사실 맥주 먹으러 가야해서 시간이 없네요.

다 먹고 살자고 하는 건데 맥주한잔하고 오겠습니다.

2탄에서 계속 하도록 할게요!

문제를 만들다보면 테스트 케이스 제너레이터가 필요하다 뭐 단순 배열이나 랜덤 숫자를 찍는건 간단한 일이지만...


그래프나 트리의 경우는 좀 화가난다. 특정 조건을 만족시키는 그래프를 생성하는게 굉장히 성가진 일이기 때문... 


이런 일을 조금 더 편하게 해주는 사이트가 있는데 그래프나 트리의 일반적인 형태의 경우는 이걸로 만들면 조금 편할 것 같다.


http://spojtoolkit.com/TestCaseGenerator/


몇가지 추가되었으면 하는 것도 있지만...


일단 이정두면 쓸만은 하지않나 싶다



====================


일단 테스트케이스를 어떻게든 만들고 나면 테스트 케이스에 해당하는 아웃풋을 만들어야하는데


소스 코드를 일일이 컴파일 붙여넣기 하거나 소스코드에 하드코딩하는 방법을 주로 사용한다. 그런데 C++은 문자열 관련 연산이 영 귀찮기 때문에 파일 이름 만들고


이런저런 짓을 하는게 좀 귀찮아서...


쉘 스크립트를 좀 짜봤다.


제너럴 하지는 않고 내가 필요한 정도만 짜놨기 때문에 실제 사용하려면 약간의 커스터 마이징이 필요할 지 모르겠다.


난 이걸로 잘 돌려 막기했다.. ㅋㅋㅋㅋㅋㅋㅋ


https://gist.github.com/choiking10/28a3dbe456203c3f92bbd05749377b36



자바는... 느리다.

그런데 생각보다 빠르다. 



일반적으로 알고리즘을 풀 때, 자바의 경우는 scanner를 자주 사용한다. 아무래도 여러가지 부가기능이 붙어있으므로 사용하기 편한면 때문에 다들 많이 사용하는 것 같다.


다만, 자바의 경우 입력이 많을 때 , scanner를 써서 시간초과가 날 수 있다. 뿐만 아니라 그때 그때 출력할 경우에도 시간 초과가 날 수 있다.


오늘의 완죤 쉬운 문제 하나를 소개하겠다. 


이름하야 수 정렬하기3  https://www.acmicpc.net/problem/10989


솔루션은 간단한데, 직접 정렬을 할 경우 시간초과에 걸리게 된다. N 사이즈가 천만이기 때문에, log조차 붙이는게 조심스럽다.


그런데 보면 최대 값이 10000이다 따라서 카운팅소트를 진행하시면 되겠다.


근데 이문제의 핵심은 이거라기보다... 


인풋을 얼마나 빨리 받을 수 있냐에 있다. 


자바용 쓸만한 빠른속도로 입력받기 소스를 소개하겠다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class Main {
    public static void main(String[] args) throws IOException{
 
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
 
        int N = Integer.parseInt(br.readLine());
        int[] arr = new int[10010];
 
        for (int i = 0; i< N;i++){
            arr[Integer.parseInt(br.readLine())]++;
        }
 
        for(int i =0; i<= 10000; i++){
            while(arr[i] != 0){
                sb.append(""+i+"\n");
                arr[i]--;
            }
            
        }
        System.out.println(sb.toString());
    }
 
}
 
cs

한꺼번에 모아서 입력을 받고 한꺼번에 모아서 출력한다. 라는 쉬운 개념인데, c++의 scanf, printf 보다 성능이 좋다.

이런 쉬운 것에 불필요한 설명을 덧붙이는 것은 좀 귀찮으니... 다들 소스코드 이해하는데 문제 없으리라 보고 이정도만 포스팅 하도록 하겠다.

다만 주의할 것이 StringBuilder를 사용하지 않고 string간의 +연산을 사용할 경우, 

자바를 아시는 분들은 아실 수도 있지만, 당연히 시간 초과가 난다. String간 + 연산에 관해서는 검색해서 찾아보시길 바랍니다. 



연산자 오버로딩


 연산자 오버로딩이란 내가 만든 사용자 정의 타입(class나 struct)에 관한 연산자를 정의하여 좀더 편하게 사용할 수 있게 만든 C++의 문법적 기능의 말한다. 


 문법

1
2
3
4
[리턴형] operator[연산자]( [연산 인자] ) {
//연산하는데 들어가는 로직
}
 
cs

example 1)

1
2
3
4
5
6
struct point {
    int x, y;
};
bool operator==(point a, point b) {
    return a.x == b.x && a.y == b.y;
}
cs

example 2)

1
2
3
4
5
6
7
struct point {
    int x, y;
    bool operator==(point b) {
           return x == b.x && y == b.y;
    }
};
 
cs


단항 연산자 오버로딩이항 연산자 오버로딩

 이 두가지 분류는 항의 갯수에 따라 단항 연산자 오버로딩인지, 다항 연산자 오버로딩인지가 정해진다. 오버로딩할 연산자의 항의 개수는 각각의 연산자들의 원래 항의 개수와 정확히 일치한다. 예를들어 /연산은 이항연산자 이므로 이항 연산자 오버로딩 해야하고 !는 단항 연산자 이므로 단항 연산자 오버로딩을 해야한다. 


더 알아 보기

이것 이외에도 아주 특이한 친구들이 몇몇 존재하는데 바로 ++나 --이다. 이 두가지 연산자를 오버로딩하는 방법에 관해서는 구글링 해보도록 하자. 현재 포스팅에서 다루기에는 너무 세세하게 들어간 것 같다.  

 
단항 연산자와 이항 연산자를 오버로딩한 모습은 아래와 같은데 -의 경우 단항연산자로도 쓰이고 이항 연산자로도 쓰이므로 좋은 예가 될 것 같아서 아래와 같이 오버로딩 해봤다. (23번째줄에서 사용) (생성자 만들기 귀찮아...)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <cstdio>
using namespace std;
struct point{
    int x,y;
 
};
point operator-(point p1){
    point ret;
    ret.x = -p1.x;
    ret.y = -p1.y;
    return ret;
}
point operator-(point p1,point p2){
    point ret;
    ret.x = p1.x - p2.x;
    ret.y = p1.y - p2.y;
    return ret;
}
int main() {
    point p1, p2;
    p1.x = 10, p1.y = 20;
    p2.x = 5, p2.y = 5;
    point p_one = -p1, p_two = p1-p2 ;
    printf("%d %d\n%d %d",p_one.x, p_one.y,p_two.x, p_two.y);
    return 0;
}
cs

output

- 10 - 20

5 15


컴파일 및 아웃풋


위의 23번째 줄은 다음과 동치이다. operator-를 일반 함수명이라고 생각한다면 더 이해하기 쉬울 것이다.

1
point p_one = operator-(p1), p_two = operator-(p1,p2);
cs



구조체나 클래스의 연산자 오버로딩전역 함수의 오버로딩


 일반적으로 전역 함수의 오버로딩은 단항 연산자의 경우 인자를 하나, 이항 연산자의 경우 인자를 두개 받는다. 이런 형태를 띄는게 전역 함수의 오버로딩인데 그 대표적인 예가 바로 위에서 보여준 예이다. 그렇다면 구조체나 클래스 안에서 구현한 연산자 오버로딩은 어떻게 다를까? 


  구조체나 클래스 안에서의 연산자 오버로딩의 경우 첫 번째 인자가 구조체나 클래스 인스턴스를 가르킨다고 생각하면 쉽다. 예제로 이해해 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <cstdio>
struct point{
    int x,y;
     point operator-(){
        point ret;
        ret.x = -x;
        ret.y = -y;
        return ret;
    }
    point operator-(point p2){
        point ret;
        ret.x = x - p2.x;
        ret.y = y - p2.y;
        return ret;
    }
};
 
int main() {
    point p1, p2;
    p1.x = 10, p1.y = 20;
    p2.x = 5, p2.y = 5;
    point p_one = -p1, p_two = p1-p2 ;
    printf("%d %d\n%d %d",p_one.x, p_one.y,p_two.x, p_two.y);
    return 0;
}
cs

output

- 10 - 20

5 15


 연산자 오버로딩이 아까와는 달리 단항 연산자의 경우는 아무런 인자를 받지 않고, 이항 연산자의 경우 인자로 받는 항의 개수가 하나가 된다. 위에서 설명했다시피 자기 자신을 첫번째 인자라고 생각하면서 동작하게 되기 때문에 단항 연산자의 항은 자기자신으로 이미 한개가 정해져 있고, 이항 연산자의 경우는 첫번째 항으로 자기 자신이, 두번째 항으로 p2가 오게된 것이다. 
 23번째 줄을 아래의 문장으로 대치해도 같은 방식으로 동작하게 된다.(의미가 같다)
1
point p_one = p1.operator-(), p_two = p1.operator-(p2);
cs


더보기

 구조체나 클래스에 종속적인 배열 인덱스 연산자 오버로딩과 대입 연산자 오버로딩

 이 부분은 현재 시점에서 다루기 적절치 않은 부분이므로 개인적으로 공부하도록 합시다 ^ㅇ^

 메모리 관리나 레퍼런스 같은 복잡한 것들이 얽혀 있습니다. 실제로 C++의 라이브러리를 구현한다던가 하는 큰 규모의 프로젝트에서 사용될 법한 내용들은 처음 하시는 분들을 위해서 생략하도록 하겠습니다. 이런게 있구나 하고 넘어가시기 바랍니다. 



 C++은 그 기본부터 C에서 온 친구로서 사실상 C에서 사용할 수 있는 것들은 대부분 C++에서도 사용 할 수 있다. 오히려 C에서 불편하던 여러가지를 개선 시킨 것이 C++이라 할 수 있는데 이 때문에 C에서 불편하던 것들이 무엇인지 안다면 C++로 넘어가면서 어떤 방식으로 개선되었는지를 직관적으로 이해하기 쉽다. 

 

이번 포스팅에서는 C에서 C++로 넘어가는 와중에 필요한 지식을 간단히 설명하려 한다. 

 

여러분들이 C에 대해 대부분의 내용을 이해했다고 가정하고 설명하도록 할 것이기 때문에 C에 대한 내용은 언급만 하고 넘어갈 예정이다. 

 

1. namespace 

 

namespace란? 이름 공간. 함수, 변수, 클래스등의 하나로 묶어서 접근을 특정 이름만으로 할 수 있도록 제한하는 키워드 

 

 C에서 사용하는 여러 라이브러리의 속을 한번 뜯어봤다면 봤을 만한 함수나 변수명이 있을 것이다. __로 시작하는 이름들이 그것인데 이런 방식으로 사용하는 근본적인 이유는 함수명이나 변수명의 중복을 피하기 위해서다. (일종의 약속이라고 보면 된다.)

 

 이런 것들은 C의 태생적 한계 때문이기도 한데 함수의 경우 그 이름을 전역으로 밖에 선언하지 못하고 따라서 여러 라이브러리를 사용할 경우 이름이 겹치는 위험이 발생할 수 있기 때문이다. 이를 방지하기 위해 C에서는 static 키워드를 제공하는데 (특정 파일 안에서만 접근 가능 ) 이것으로는 부족한 것이 현실이다. 

 

 이를 해결하기 위한 좋은 방법으로 C++에서 제공하는 namespace기능을 이용할 수 있다. 이 기능을 사용하면 변수명이나 함수명이 겹치더라도 잘 처리할 수 있다. 

 

 이 namespace라는 것을 사용하는 방법은 아래와 같다. 

 

 

 

 일반적으로 C++로 프로그램을 짤 때 많이 사용하는 STL(Standard Template Library)은 모두 std라는 namespace에 묶여있으며 이 안에는 최대 값을 구해주는 함수가 있다. 이 함수는 STL library중에서도 algorithm 헤더파일에 들어 있는데 이를 사용하는 모습은 아래와 같다. 

 

1
2
3
4
5
6
#include <stdio.h>
#include <algorithm>
int main(void){
    int max = std::max(10,20);
    printf("%d",max);
}
cs

 

 

2. struct, class

 

 struct, class란? 여러 자료형을 한곳에 묶어 관리하기 위한 하나의 '구조'로 필요에 따라 접근을 제한 하고, 해당하는 자료를 처리하기 위한 루틴을 거치도록 만들 수 있다. 

 

 일반적으로 struct는 data에 관한 자료를 다루기 위해 사용하며, class는 그 외의 다양한 자료를 다루기 위해 사용한다. (ADT 같은 것들)

 

 C에서도 struct라는 키워드가 있다. 이는 C언어에서 제공하는 아주 강력한 기능으로 그 쓰임세는 정말 다양하여 모두 나열할 수는 없을 것이다. 하지만 그 대부분의 쓰임세는 struct의 특징인 여러 자료형을 한 곳에 묶어 (의미적으로, 공간적으로) 관리하기 편하게 만들었다는 점에서 기인한다. 하지만 C의 이런 방식은 한계가 존재하는데 바로  누구나 해당 자료형의 데이터에 접근하고 수정할 권한이 있다는 것이다. 

 

 ADT의 예를 들어보자. ADT에는 추상화라는 개념이 있다. '추상화'란 사용자가 그 내부의 복잡한 구현을 알 필요없이 그 기능들을 가져다 쓰게 함으로 써 실질적으로 필요로 하는 것을 구현하고 설계하는데 집중 하게하는 것이다. C에서 이를 쉽게 구현하는 법은 구조체에 담아 관리하고 이 구조체를 다루는 함수를 제공하는 방식인데 이 방식에는 약간의 문제가 있다.

 그 문제는 접근을 제한할 수 없기 때문에 발생하는데 대표적으로 struct의 field에 사용자가 접근하여 값을 바꾸는 것이다. 

 

ADT의 가장 대표적인 예인 double linked list를 예로 들어보자.  

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
 
struct linked_node {
    int data;
    struct linked_node * prev, * next;
};
struct linked_list {
    int size;
    struct linked_node * front* back;
};
bool linked_empty(struct linked_list * list){
    return list->size == 0;
}
int main(void){
    struct linked_list list;
    linked_init(&list);
    list.size = 1000;
    if(linked_empty(&list)){
        printf("empty");
    }else printf("not empty");
}
cs

 

 

위 코드의 linked_empty()라는 함수는 해당 리스트가 비어있다면 true를 비어있지 않다면 false를 리턴하는 C 코드이다.  만약 이 때 어떤 사용자가 main 처럼 마음대로 접근을 해버렸다고 해 버리자. 

 

 그렇다면 이 ADT는 원래의 역할을 제대로 다하지 못할 것이다. 

 

이 처럼 특정 데이터에 접근하는 것을 제한할 필요성이 있는데 이를 C++에서는 접근제어자로 해결할 수 있다. 접근 제어자는 크게 3가지 종류가 있는데 아래와 같다. 

  • private
    • 외부에서 접근이 불가능하고 내부에서만 접근이 가능하다. 
  • public
    • 외부에서 접근이 가능하다.
  • protected
    • 상속과 관련된 내용. 추후에 다룸

 위 키워드들은 아래와 같이 사용할 수 있다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
 
 
struct linked_list {
private :
    int size;
 
    struct linked_node {
        int data;
        struct linked_node * prev, * next;
    }  * front* back;
public :
    linked_list() {
        size = 0;
        front = back = (linked_node *)0;
    }
    bool empty(){
        return size == 0;
    }
};
int main(void){
    linked_list list;
 
    list.size = 1000//에러
 
    if(list.empty()){
        printf("empty");
    }else printf("not empty");
}
cs

 13~16은 생성자라는 친구로 C에서는 없는 개념이다. linked_init에 해당하는 친구다. 

 

 24번째 줄에서 에러가 나는 것을 확인 할 수 있는데 private로 접근이 제한되었기 때문이다. 이렇게 잘못된 코드를 런타임 에러나 잘못된 동작이 아니라 컴파일 에러로 찾아 낼 수 있다는 것은 더 안전한 프로그램을 개발 할 수 있게 만들어준다. 

 

 상속이나 생성자, 소멸자에 관한 내용은 다음번에 다시 다루기로 하겠다. (지금은 필요 없음 ㅎ)

 

 c++에서 struct와 class는 큰 차이가 없는데 이는 c++이 객체지향적이기도하고 절차지향적이기도 한 과도기적 언어이기 때문이다. 다른 언어의 경우 이 두 키워드를 차이를 둬서 다루게 된다. 

 

 c++에서 두 키워드의 유일한 차이

  •  struct field의 default 접근제어자가 public
  •  class field의 default 접근제어자가 private

더 자세한 내용은 다음에 다루도록 하겠다. 

 

3. template

 

template란? template이란 단어 그 자체의 이름 그대로 형틀이다. 특정한 형틀을 가지고 자료형,함수등 새로운 것들을 찍어내는 것이라고 보면 된다. input으로 자료형을 넣으면 새로운 자료형, 함수, 클래스 등을 찍어 낼 수 있다.

template은 기존의 C언어에서 대응되는 개념이 거의 없는데 비슷한 것을 꼽아보라면 void *형이라고 보면 될 듯 싶다. 이는 void *보다 더 많은 정보를 담고 있는 친구이다. 

 

여러분은 C언어의 stdlib.h에 존재하는 qsort라는 함수를 사용한 경험이 있을 것이다. 이 함수는 quick sort를 구현해 놓은 친구로 어떤 배열형이든 받아 정렬해주는 함수이다. 이 함수의 원형은 다음과 같다. 

 

1
2
void qsort (void *base, size_t num, size_t size,
             int (*compare)(const void *const void *))
cs

혹시 모르는 사람들을 위해서 설명하자면 어떤 배열(base)과 그 배열이 담고있는 원소의 갯수(num), 그리고 원소 하나의 바이트수(size)와 각각의 배열을 어떻게 비교할지 결정하는 비교함수(compare)를 인자로 넘기면 그 배열을 quick sort로 정렬해주는 함수다. 

 

 C언어에서는 이런 임의의 배열을 표현할 방법이 없기 때문에 void *형이라는 꼼수를 사용해서 이를 구현해야 했다. 반면 C++에서는 이런 임의의 무언가를 표현할 좀더 직관적이고 안전한 방식을 제공하는데 그것이 바로 template이다. 

 사실 이런 template는 class부터 기본 타입, 때로는 상수도 들어갈 수 있으며 여러가지 장난질을 칠 수 있다. 이런 template으로 함수나 클래스등을 찍어 낼 수 있으며 이러한 자세한 내용에 관해서는 나중에 다루도록 하겠다.

 

 

template의 사용법은 대체로 "template이름<자료형>" 형태로 사용하게 된다. 위의 경우에는 template이름은 my_sort고 자료형들은 int나 double이 들어간 것이다. 때로는 이 <> 를 사용하지 않고도 사용할 수 있는데 이는 컴파일러가 자동으로 타입을 추론할 수 있는 경우에 한한다.   

 

template의 자세한 구현방법에 대해서는 다음에 더 자세한 내용을 다룰 것이다.

 

 

4. STL container

 

 STL이란? Standard Template Library의 약자로 C++에서 제공되는 표준화된 라이브러리다. 이 라이브러리는 쓰레드, 알고리즘, 특정한 ADT등 들을 다루기 위한 여러가지 모듈들을 이미 구현해놓았다.

 Container란? ADT들을 실질적으로 구현해놓은 것들이다.

 아마 다루고 넘어갈 것들로는 아래와 같다.

  • vector
  • deque
  • list
  • stack
  • queue
  • priority_queue
  • bitset
  • map
  • set
  • unordered_map
  • unordered_set
  • multiset
  • multimap
  • unordered_multiset
  • unordered_multimap

 

 

딱히 필요해서 정리하는 것은 아닌데 이런 것이 정리하고 공유하는게 다른 분들이 공부하기 더 쉬울 것 같아 포스팅 한다.


1. dovelet : www.dovelet.com

더블릿은 부분유료 사이트이다. 모든 문제를 볼수는 있다. 다만 1,2,3계단만 공짜로 채점 받을 수 있다. 즉 4단계 이상의 문제를 제출해 볼 수는 없다. 1,2,3단계는 기본적인 입출력, 반복문등을 연습하는 기초적인 단계이므로 알고리즘을 공부하기로 마음 먹은 사람에게 거의 필요없는 문제들이 많기 떄문에( 물론 코딩 속도를 올리는 용도로는 효과적이라는 이야기도 있다. ) 사실상 유료사이트라고 봐도 될 것 같다.


특징

장점

      1.  계단별로 특정 알고리즘의 설명, 수도코드, 소스코드등이 첨부되어 있어 혼자 공부하기 쉽다. 
      2. 특정 알고리즘을 배운 다음에 그 밑에 그 알고리즘으로 풀리는 문제들이 있다. 그 문제들을 풀어보며 감을 익힐 수 있다. 
      3.  만약 제출한 코드가 틀렸을 경우 틀린 testcase를 볼 수 있다. 

단점

      1.  제출한 코드가 틀렸을 경우에 틀린 testcase를 보여주는게 항상 좋은 것은 아니다.(실전 처럼 연습할 때 불편할 경우가 많다.)
      2.  테스트케이스가 빡빡하지 않다. 통과되지 말아야할 소스코드가 통과되는 경우가 왕왕 있었던 것으로 기억한다.
      3.  제대로 공부를 할 요량으로 이 사이트를 사용하려면 3만원? 정도되는 가격을 지불해야한다. (평생 사용 요금이다.)



2. 백준 온라인 저지 : www.acmicpc.net 

백준은 완전 무료사이트로 모든 문제를 공짜로 풀어볼 수 있다. 유아이도 상당히 이쁜 편이다. 직관적이고 상당히 많은 문제를 풀어 볼 수 있다. acmicpc관련 문제 + 올림피아드 관련 문제들이 많아 이 쪽 준비를 하는 사람들에게는 상당히 도움이 되는 사이트다. 질문 게시판들도 있어 모르는 문제를 물어볼 수 있다. 갓갓갓님들이 잘 답변해주신다고....



특징

장점

      1. 공짜다.
      2. 문제양이 상당히 많다.
      3. UI가 이쁘다.
      4. 갓갓갓님들이 주로 상주하는 사이트이고, 또 질문을 올릴 경우 이 갓갓갓 분들이 답변을 잘해주신다.
      5. 테스트 데이터가 빡빡한 편이라 어셉이 나지 말아야할 코드가 어셉이 나는 경우는 거의 없다. 있다고 해도 사람들이 찾아서 데이터를 보강해준다.  
      6. 그룹을 만들어 아는 사람들끼리 가상의 대회를 열어볼 수 있다.

단점

      1.  아직 분류 기능이 활성화 되어 있지 않아서 분류되어 있는 문제가 적다. 특정 알고리즘으로 푸는 문제를 분류탭에서 찾아보면 문제가 너무 적거나 말도안되게 어렵거나... 
      2. 친구기능이 없다. ㅠ

기타

      1. icpc.me/[문제번호] 라는 url로 접속하면 해당 문제로 바로 이동 가능하다.
         예) 자물쇠 문제 : icpc.me/1514
      2. BOJ 슬랙이 있다. 질문을 게시판으로 해도되지만 슬랙에 올려도 된다. 슬랙에 올리는 편이 더 자세한 답변을 들을 수 있는 것 같다.  



3. UVA, live archive: https://uva.onlinejudge.org/ ,https://icpcarchive.ecs.baylor.edu/

 되게 짜증나는 외국 사이트인데 처음 시작하는 사람에게는 별로 추천해주고 싶지는 않다. 문제양은 정말 많지만... 데이터 자체가 틀렸을 경우가 왕왕이아니라 아주 많다. (정답인 코드가 틀린다던가 하는) 뿐만아니라 개행문제등을 이유로 틀렸습니다가 뜨는 문제들이 너무많다.(스트레스... ) 


특징

장점

      1. 공짜다.
      2. 문제양이 상당히 많다.
      3. acmicpc관련 문제는 거의 대부분 있다.

단점

      1. 내가 알기로 문제 분류기능 따위는 없다. UI도 불편... 
      2. acm문제들 중 예선문제는 수록되어 있지 않다.
      3. acm문제들이 대부분 수록되어있으나 데이터가 틀린 경우가 너무 많다.
      4. 틀리기만 하는 것도아니고 데이터가 약한 것도 너무 많다.
      5. 개행등으로 틀렸습니다가 뜨는 경우가 많다.

기타

      1. 가상의 대회를 열어볼 수 있다. https://icpcarchive.ecs.baylor.edu/uhunt/
      2. 근데 위에 저게... 시간이나 문제링크등이 이상하게 되어있다. 짜증남... 


4. codeforces, topcoder : www.codeforces.com  , www.topcoder.com

 온라인 저지라기보다 컨테스트 사이트다. codeforces는 러시아 것이고 topcoder는 미국 것이다. 문제는 둘다 영어로 제공되며 게임처럼 레이팅을 경쟁할 수 있다. 레이팅별로 색깔이 달라지는데 빨간색, 노란색, 보라색 티어(?)가 있다. 롤로 치면 마스터, 다이아, 플레티넘 같은 것이라고 보면 된다. 레이팅 올리는 재미가 있으며 정규 컨테스트에서만 레이팅이 변경된다. 둘다 가상의 컨테스트를 열어볼 수 있고 DIV1/DIV2로 나누어져있는데 DIV1은 상위리그, DIV2는 하위리그다. 처음 시작 하는 분들은 DIV2 대회로 시작하시면 되겠다.

특징

장점

      1. 공짜다.
      2. 게임처럼 참가할 수 있다. 롤 레이팅 올리는 느낌이다. 
      3. 에디토리얼이라고 문제풀이가 제공된다. 따라서 보면서 공부할 수 있다.
      4. 둘다 핵 개념이 있는데 컨테스트중에 제공되는 테스트 케이스는 약한 테스트 케이스다. 따라서 어셉이나도 최종적으로 어셉이 아닐 수 있다. 이런 부분 때문에 각별히 예외나 시간복잡도 신경써야한다. 다른사람이 여러분의 소스코드를 읽고 틀린 데이터를 만들어 여러분의 소스코드를 공개할 수 있다.
      5. 반대로 문제를 잘 못풀어도 핵을 잘해서 점수를 딸 수 있다. 다른 사람의 소스코드에서 예외를 찾아 던지면 추가 점수를 얻을 수 있다.  

단점

      1.  소스코드를 읽기가 힘들다. 각종 define과 typedef의 연속... 
      2.  콘테스트에서 영어에 의해서 디버프를 받을 수 밖에 없다. 부족한 영어실력을 알고리즘 실력으로 극복해야한다. ㅠㅠ
      3.  핵당하면 괜히 기분이 나쁘다.
      4. 사이트가 빠른 편은 아니다. (아무래도 한 컨테스트당 몇천명의 사람이 몰리기 때문인 것 같다.) 




자세히 소개는 안했지만 다른  좋은 곳들도 많다. 

주로 사용하는 사이트만 리뷰하고 자주 사용하지 않은 사이트들은 정리하지 않았다. 잘못된 정보를 줄 수도 있기 때문에...

아래는 그외 사이트들이다.


알고스팟 : www.algospot.com 

수학관련 문제 - 프로젝트 오일러 :  http://projecteuler.net/ 

수학관련 문제 - 프로젝트 오일러(한국) : http://euler.synap.co.kr/ 

아주대학교 알고리즘 온라인 저지 라비다 : http://www.lavida.us/ 



+ Recent posts