파이썬 알고리즘 : 가운데를 말해요

백준1655

2023년 7월 28일 알고리즘 문제풀이

백준 1655번

문제 링크

1차 시도

나의 생각

양 끝과 중간이라는 생각에 deque을 사용하면 어떨까 생각하고 코드를 작성했다. 생각해보니 중간과 끝 사이에 새로운 숫자를 집어넣어야 하는데… 다행히 우선순위 큐, 최소 heap이 생각났지만 너무 늦게 생각나서 코드 작성을 못했다. 다음 시도에선 해봐야지.

결과

오답

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import sys
from heapq import heappop, heappush, heappushpop
n = int(sys.stdin.readline())
big = []
mid = []
small = []
for _ in range(n):
    num = int(sys.stdin.readline())
    if not mid:
        mid.append(num)
        print(num)
        continue

    if num >= mid:
        tmp = heappushpop(big,num)
        mid.append(tmp)
        print(mid[0])
    else:
        tmp = heappushpop(small,-num)
        mid.append(-tmp)
        mid.sort()
        print(mid[0])

2차 시도

나의 생각

heap을 사용하였다. 가운데 값을 기준으로 크냐 작냐에 따라 경우가 나눠질거라고 생각해서 가운뎃값보다 작은 heap, 가운뎃값을 담은 heap, 더 큰 heap 세개로 나누어서 생각했다. 하지만 문제에서 나왔듯 가운데 값이 전체 숫자의 갯수에 따라 2개일 수도 한 개 일수도 있어서 가운데를 담는 배열을 1로 유지해야할지, 혹은 큰 배열과 작은 배열의 수를 갖게 유지해야할 지 여러모로 헷갈렸다. 차라리 하나를 우직하게 했으면 정답은 나왔으려나.. 여러 생각에 헤메다가 시간만 허비하고 코드를 완성하지 못했다.

결과

오답

코드

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
import sys
from heapq import heappop, heappush, heappushpop
n = int(sys.stdin.readline())
large = []
mid = []
small = []
for _ in range(n):
    num = int(sys.stdin.readline())
    if not mid :
        heappush(mid,num)
        print(num)
        continue

    if len(mid) == 2:
        min_val = min(mid)
        if num >= min_val:
            tmp = heappushpop(large,num)


        heappush(small,tmp)
        tmp = heappop(mid)
        heappush(large,mid[0])
        mid[0] = tmp

    else:
        heappush(mid,num)

3차 시도

나의 생각

우선 처음 3개의 숫자는 따로 조건을 작성하였다. 그리고 2차 시도에서 고민하던 문제는 항상 가운뎃값을 담는 배열을 1개로 유지하기로 하였다. 코드를 작성하다보니 largesmall이라는 배열은 같거나, large가 1만큼 크기가 더 큰 경우 밖에 없다는 것을 깨달았다. 내가 가운뎃값의 배열을 1개만 담기로 설정해놨기 때문에 전체 숫자의 갯수가 홀수면 두 배열의 길이는 같을 것이고, 짝수라면 가운뎃값 후보 2개 중 작은 값이 가운뎃 값을 위한 배열에 들어가고 남은 값은 더 크기 때문에large배열에 들어가야하면서 생긴 규칙이다. 이를 바탕으로 다양하게 조건을 나누었지만 어디가 문제인지 indexerror와 typeerror가 발생하여 오답처리 되었다. 내가 생각해도 코드가 너무 복잡하고 더럽다.

결과

오답

코드

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import sys
from heapq import heappop, heappush, heappushpop

n = int(sys.stdin.readline())
large = [] # 가운뎃값보다 큰 배열
mid = [] # 가운뎃값 담는 배열
small = [] # 가운덱값보다 작은 배열, 최소힙이기 때문에 음수로 바꿔서 넣고 뺄 예정
cnt = 0 # 초반 3개를 위한 설정
for _ in range(n):
    cnt += 1 # 입력한 숫자 체크
    num = int(sys.stdin.readline())
    if cnt == 1: # 처음 들어온 숫자는 무조건 출력
        heappush(mid,num) # 1개뿐일땐 가운데
        print(num)
    elif cnt == 2: # 두번째로 들어올때는 둘 중 작은 수
        tmp = heappushpop(mid,num) # 두 숫자 모두 mid에 들어있음
        print(tmp)
        heappush(large,heappop(mid)) # 두 숫자 중 큰 것은 large로 옮김
        heappush(mid,tmp) # 작은 것은 mid에 둔다.
    # 현재 mid에 한 개, large에 1개 들어있는 상태. 이제부턴 일반화 가능하므로 cnt는 신경안씀
    else:
        if len(large) == len(small): # 두 배열의 크기가 같다면 mid에는 1개뿐이므로 다음에 수가 들어오면 가운데값이 2개가 된다.
            if mid[0] > num: # 현재 가운뎃값보다 작은 값이 들어오면?
                num *= -1
                tmp = heappush(small,num) # 음수로 변환해 small에 넣은 후 pop하여 최대힙을 찾는다.
                tmp *= -1
                tmp = heappushpop(mid,tmp) # 찾은 최대힙을 mid에 넣고 최소힙을 찾는다.
                print(tmp) # 가운데 값중 작은 수를 출력해야하니 그대로 출력
                heappush(large,heappop(mid)) # mid에 남은 수는 큰 수 이므로 large로 넣는다.
                heappush(mid,tmp) # 아까 뺀 가운데 값을 다시 넣어준다.
                # 이 상황에서는 mid의 길이는 1이고 large의 길이가 small보다 1 더 길다.
            else: # 가운뎃값보다 큰 값이 들어왔다.
                tmp = heappush(large,num)  # large배열에 넣는다.
                tmp = heappop(mid) # 어차피 large배열에서 최소힙을 구해봤자 둘 중 더 작은 현재 mid의 원소를 출력해야하므로 굳이 꺼내지 않는다.
                print(tmp) # 가운뎃값 중 작은 수를 출력
                heappush(mid,tmp)
        else: # large와 small이 같지 않을 때는 항상 large가 더 크다. 가운데 값 둘 중 작은 수를 출력하고 큰 수는 large에 넣도록 로직이 되있으므로.
            if mid[0] > num: # 현재 가운뎃값보다 더 작은 수가 들어옴
                num *= -1 # 음수로 변환
                heappush(small,num) # 가운뎃값은 하나므로 배열의 길이가 1 작은 small 에 넣고 현재 가운뎃값 그대로 다시 출력하면된다.
                tmp = heappop(mid)
                print(tmp)
                heappush(mid,tmp)
            else: # 현재 가운뎃값보다 더 큰 수가 들어옴
                tmp = heappop(mid) # 현재 가운뎃값은 음수로 변환해 small배열에 넣는다.
                tmp *= -1
                heappush(small,tmp)
                tmp = heappushpop(large,num) # 새로 들어온 수를 large 배열에 넣고 최소힙을 구한다.
                heappush(mid,tmp) # 구한 최소 heap을 새로운 가운뎃값으로 설정
                print(tmp)

4차 시도

나의 생각

코드가 더러워진 가장 큰 문제는 heap의 최솟값을 출력할때 heappop으로 찾아내어 출력하고 다시 heappush로 넣어줬기 때문이다.

혼자 테스트해본 결과 내가 만든 배열에 `heappop`,`heappush`를 사용해서 배열을 수정해주면 0번 index가 항상 최솟값을 나타낸다!

이걸 모르고 heappop,heappush를 해댔으니 코드가 더러워질 수밖에.. 기존의 로직은 유지한채 코드만 잘 정리했더니 훨씬 깔끔해졌고 바로 정답이였다. 참고로 최솟값이 튀어나오기 때문에 최댓값을 내야하는 small 배열은 넣고 뺄 때 음수로 변환해주었다.

결과

정답

코드

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
import sys
from heapq import heappop, heappush, heappushpop
n = int(sys.stdin.readline())
large = []
small = []
for _ in range(n):
    num = int(sys.stdin.readline())
    if not large and not small:
        print(num)
        heappush(large,num)
    elif len(large) == len(small):
        if -small[0] < num:
            heappush(large,num)
            print(large[0])
        else:
            tmp = heappushpop(small,-num)
            heappush(large,-tmp)
            print(large[0])
    else:
        if num >= large[0]:
            tmp = heappop(large)
            heappush(small,-tmp)
            heappush(large,num)
            print(-small[0])
        else:
            heappush(small,-num)
            print(-small[0])