문제) 방 배정

 

해당 문제는 초등학교 학생들이 단체로 수학여행을 하게 되어 남녀 & 학년별로 방을 배정 하려고 했을 때 최소 방을 몇개 잡아햐 하는 지를 구하는 문제 였다

 

처음 문제를 봤을 때 파이썬의 딕셔너리 키 값을 학년으로 잡고 그후 남녀를 구분하는 리스트 값을 넣으려고 했다

 

초등학생 = {
'1학년' : {
	'남학생' : 0,
    '여학생' : 0
    },
'2학년' : {
	'남학생' : 0,
    '여학생' : 0
    },   
    .....
}

 

대략 적으로 위의 방식으로 풀면 어떨까 라는 생각을 하다가 굳이라는 생각이 들게 되었다. 그래서 DP 방식으로 학년 별 남녀 그룹만큼 배열을 만들고 0으로 초기화 하여 해당 문제를 풀게 되었다

 

더보기

정답 주의!!!

 

import sys
N, K = map(int, sys.stdin.readline().split())
stds = [0] * 12
for _ in range(N):
    s, ss = map(int, sys.stdin.readline().split())
    stds[((ss * 2) - (1 - s)) - 1] += 1

answer = 0
for std in stds:
    answer += std // K
    if std % K != 0:
        answer += 1

sys.stdout.write(f'{answer}')

 

'Python > Python 문제' 카테고리의 다른 글

백준) queuestack  (0) 2024.08.14
백준) 풍선 터뜨리기  (0) 2024.08.14
백준) 골드바흐 파티션  (0) 2024.08.09
프로그래머스) 쿼드압축 후 개수 세기  (0) 2024.08.07
백준 ) 게리맨더링  (0) 2024.08.05

문제) queuestack

 

해당 문제는 문제를 이해하기 까지 조금 시간이 오래 걸린 문제였다. 한글로 이루어진 문제인데 왜 해독이 어려운지...

어찌 되었든 해당 문제는 문제의 설명에 나와 있는 데로 큐 또는 스택을 pop을 하여 마지막에 pop 이된 변수를 따로 저장하여 따로 저장된 변수들을 한줄로 출력해야 하는 문제였다

 

그리고 주어진 입력들이 먼저 

N이 주어지는데 이건 큐 또는 스택의 갯수를 말하는 것이고

다음 2번째 줄은 각각의 큐 또는 스택을 결정하는 명령어였다. 0이면 큐이고 1이면 스택이다

다음 줄은 각각의 큐 또는 스택에 들어갈 값이였다

그후 수열의 크기 M이 주어지고 

다음 줄에 수열의 원소들이 주어지게 되는 것이다

 

이 것을 이해하기 까지 좀 걸렸다...

 

문제 설명이랑 입력값과 출력 값을 비교하여 왜 저렇게 되지 하면서 계속 고민만 하다가 입력에 대한 설명을 다시 차근히 읽고 난뒤에야 이해가 되었다

 

문제를 이해하고 생각보다 코드를 빠르게 쓸 수 있었다(복선인가...)

 

더보기
더보기

실패코드

import sys
from collections import deque

N = int(sys.stdin.readline().strip())
queuestack = []
for a, b in zip(list(map(int, sys.stdin.readline().split())), list(map(int, sys.stdin.readline().split()))):
    if a == 0:
        queuestack.append(deque([b]))
    else:
        queuestack.append([b])
M = int(sys.stdin.readline().strip())
C = list(map(int, sys.stdin.readline().split()))
answer = []
for x in C:
    add_x = x
    for check in queuestack:
        check.append(add_x)
        if str(type(check)) == "<class 'list'>":
            add_x = check.pop()
        else:
            add_x = check.popleft()
    answer.append(add_x)

sys.stdout.write(f'{" ".join(map(str, answer))}')

의식의 흐름대로 입력에 대한 설명 + 문제 설명의 로직대로 코드를 제출을 하였지만 시간초가나면서 실패하게 되었다.

일단 for문이 2개이기에 이것의 시간 복잡도는 O(N^2)이다

 

그럼 최소 O(N)으로 바꿔야지만 통과가 된다는 의미이기도 하다

그럼 일단 필요없는 스택 - 삽입 후 출력하기에 결국 삽입한 값이 나오기에 스택은 불필요하다는 것을 알 수 있다. 그렇기에 불필요한 스택에 대한 정보는 삭제 처리하였지만 그래도 시간초가 난다...

 

그러다가 생각해보니깐 결국은 먼저 하나의 큐에 값을 앞에서부터 넣으면 된다는 것을 알게 되었다

왜냐하면 1,2,3을 문제의 설명되로 한다고 했을 때 3이 나오게 하면(큐 기준) 되는 것이다 만약 스택으로만 이루어졌다고 하면 입력받은 순서대로 뽑아내면 되는 것이다

 

더보기
더보기

정답 주의

import sys
from collections import deque

N = int(sys.stdin.readline().strip())
que = deque([])
for a, b in zip(list(map(int, sys.stdin.readline().split())), list(map(int, sys.stdin.readline().split()))):
    if a == 0:
        que.appendleft(b)

M = int(sys.stdin.readline().strip())
C = list(map(int, sys.stdin.readline().split()))
answer = []
for x in C:
    que.append(x)
    answer.append(que.popleft())

sys.stdout.write(f'{" ".join(map(str, answer))}')

 

'Python > Python 문제' 카테고리의 다른 글

백준) 방 배정  (7) 2024.08.30
백준) 풍선 터뜨리기  (0) 2024.08.14
백준) 골드바흐 파티션  (0) 2024.08.09
프로그래머스) 쿼드압축 후 개수 세기  (0) 2024.08.07
백준 ) 게리맨더링  (0) 2024.08.05

문제 ) 풍선 터뜨리기

 

해당 문제는 T의 사람이 순서대로 원을 그리고 있을 때 각자가 가진 풍선의 번호 대로 터트릴 때 풍선이 터진 인원의 순서대로 출력 해주면 되는 문제이다

 

여기서 중요한건 +는 오른쪽 -는 왼쪽이며 터진 인원은 건너띄워야 한다는 것이다

 

더보기

실패코드

import sys

T = int(sys.stdin.readline().strip())
lst = list(map(int, sys.stdin.readline().split()))
check = [False] * T
i = 0
answer = []
while lst != check:
    if i >= T:
        i %= T
    if lst[i] == False:
        i = i + 1 if i > 0 else i - 1
    else:
        if i < 0:
            i %= T
        answer.append(i + 1)
        lst[i], i = False, i + lst[i]


sys.stdout.write(f'{" ".join(map(str, answer))}')

 

일단 문제를 단순히 접근 했을 때 먼저 1번은 터뜨려 져야 하기에 1번은 맨앞에 위치해야 되고 이후 순서를 찾는 것이다.

풍선이 터진 사람의 풍선 번호를 담을 변수 i와 터진 순서를 담을 빈 리스트 answer를 선언 하고 이후 i의 위치로 이동하는데 이때 i 위치가 이미 터진 사람의 위치면 한칸 옆으로 이동하게 작성하였다

 

이렇게 되었을 때 테스트케이스는 정상적으로 동작 됨을 확인 하였다.

하지만 제출시 실패를 하게 되었다.

 

다른 테스트케이스를 찾다가 

# 입력
10
1 -2 3 -4 5 -6 7 -8 9 -10

# 출력
1 2 9 3 6 5 7 8 10 4

 

이렇게 주어진 테스트케이스를 찾아서 해당 코드에 이렵했을 때 출력이 정상적으로 나오지 않음을 확인 하게 되었다.

이유를 찾다보니 i로 이동을 한번에 하고 그곳에 터진 사람인지 체크를 하기에 문제가 생김을 확인 하였다.

 

그렇다면 i로 이동을 1씩 증가 혹은 감소 하는 방향으로 하여 이동하는 자리에 터진 사람이 체크 하는 방향으로 코드를 수정하게 되었다.

 

문제를 확인 한 부분으로 코드를 수정하고 제출 하였을 때 정상적으로 통과가 됨을 확인 하였다.

 

더보기

정답 주의!!!

# 풍선 터뜨리기
# https://www.acmicpc.net/problem/2346
import sys

T = int(sys.stdin.readline().strip())
lst = list(map(int, sys.stdin.readline().split()))
# 전부 체크 했는 확인 하는 변수
check = [False] * T
# i : 칸이동 변수, j : 현재 위치 + 이동되는 위치
i, j = 0, 0
# 순서를 담을 리스트
answer = []
while lst != check:
    # 이동할 횟수 체크
    if i != 0:
        # 이동 시 i 양수이면 한칸 앞으로 음수이면 한칸 뒤로 이동
        j = j + 1 if i > 0 else j - 1
        # j의 절대값이 리스트의 크기보다 큰지 확인 하여 크면 mod 연산
        if abs(j) >= T:
            j %= T
        # j가 이동한 곳이 이미 사용했던 곳이면 이동 횟수 추가
        if lst[j] == False:
            i = i + 1 if i > 0 else i - 1
        # 이동 횟수 줄이기
        i = i - 1 if i > 0 else i + 1

    else:
        # 이동 한 곳이 음수이면 뒤에서 꺼구로 세야 하기에 mod 연산
        if j < 0:
            j %= T
        # 리스트는 0부터 시작하기에 찾은 index 값에 1 추가 하여 저장
        answer.append(j + 1)
        # 다음 이동할 곳을 찾고 현재 위치를 사용 처리
        lst[j], i = False, lst[j]

sys.stdout.write(f'{" ".join(map(str, answer))}')

'Python > Python 문제' 카테고리의 다른 글

백준) 방 배정  (7) 2024.08.30
백준) queuestack  (0) 2024.08.14
백준) 골드바흐 파티션  (0) 2024.08.09
프로그래머스) 쿼드압축 후 개수 세기  (0) 2024.08.07
백준 ) 게리맨더링  (0) 2024.08.05

문제 ) 골드바흐 파티션

 

해당 문제에서 요구하는 사항은 짝수 숫자 N을 주어 졌을 때 N 보다 작은 소수의 두 합이 N이 되는 가짓수를 출력해 달라는 것이다. 이때 3 + 5, 5 + 3은 하나로 취급 한다.

 

문제 지문 자체는 어렵지 않으나 이제 어떤 식으로 해서 가짓수를 출력해야 될지가 문제였었다.

차근차근 고민은 및 손으로 규칙이 없나 찾아보다가

6 = 1

8 = 1

10 = 2

12 = 1

이라는 테스트 케이스를 보고 곰곰히 생각을 해보았다

6 = 3 + 3, (2 + 4, 2는 소수이지만 4는 소수가 아니다)

8 = 5 + 3, (2 + 6, 2는 소수이지만 6은 소수가 아니다)

10 = 7 + 3, 5 + 5, ( 2 + 8, 2는 소수이지만 8은 소수가 아니다)

12 = 7 + 5 , (11 + 1, 1은 소수가 아니므로 탈락)

 

해당 식을 적어보니 확실히 규칙이 보였다. 구하고자하는 N - sosu = min_sosu 가 되면 된다

만약 N - sosu != min_sosu이면 카운트를 안하면 된다.

 

이렇게 일단 숫자의 가짓수를 체크에 대한 규칙을 찾았고 이제 소수를 구하기만 하면 이문제는 해결이 될걸로 생각이 된다.

 

이 때까지 소수를 구하는 문제를 많이 풀기도하여 쉽게 생각을 하고 소수를 구하는 식을 작성한 뒤 문제를 풀었는데 어찌된 영문이지 시간 초과를 하게 되었다.

 

더보기

이떄까지 내가 주로 사용한 소수 구한 코드

dp = [0] * 1000001
dp[2] = 1
for i in range(2, 1000001):
    is_ok = True
    for j in range(2, int(i ** 0.5) + 1):
        if i % j == 0:
            is_ok = False
            break
    if is_ok:
        dp[i] = 1

 

전체 코드

import sys
dp = [0] * 1000001
dp[2] = 1
for i in range(2, 1000001):
    is_ok = True
    for j in range(2, int(i ** 0.5) + 1):
        if i % j == 0:
            is_ok = False
            break
    if is_ok:
        dp[i] = 1

T = int(sys.stdin.readline().strip())
for _ in range(T):
    N = int(sys.stdin.readline().strip())
    count = 0
    visited = {}
    for idx in range(N):
        if dp[idx] == 0: continue
        if dp[N - idx] == 1 and N - idx >= idx:
            count += 1

    sys.stdout.write(f'{count}\n')

 

소수구하는 식을 제외하고는 시간초가 날만한 부분이 전혀 보이지 않았다.

그렇다면 소수를 구하는 방법이 너무 오래 걸린다는 건데 이를 현재의 나로써는 생각하기 어려워 어쩔 수 없이 구글링을 통하여 좀더 빠른 소수구하는 방식을 손에 넣을 수 있었다.

 

더보기

향상된 소수 공식 코드

p = [1] * 1000001
sosu_list = []
for i in range(2, 1000001):
    if dp[i] == 1:
        sosu_list.append(i)
        for j in range(i*2, 1000001, i):
            dp[j] = 0

 

의 소수를 구하는 부분은 일단 전체 값을 일정한 값으로 통일을 한 뒤 통일한 값으로 된 자리이면 소수이고 이후 소수의 배수는 전부 다른 값으로 대체하여 중복 검사를 최대한 피하는 방식이다

 

찾은 방식으로 기존 코드의 소수 구하는 부분에 적용을 한 뒤 제출을 하니 무사히 코드가 통과됨을 확인하였다.

 

더보기

전체 코드

import sys
dp = [1] * 1000001
sosu_list = []
for i in range(2, 1000001):
    if dp[i] == 1:
        sosu_list.append(i)
        for j in range(i*2, 1000001, i):
            dp[j] = 0

T = int(sys.stdin.readline().strip())
for _ in range(T):
    N = int(sys.stdin.readline().strip())
    count = 0
    visited = {}
    for sosu in sosu_list:
        if sosu > N: break
        if dp[N - sosu] == 1 and N - sosu >= sosu:
            count += 1

    sys.stdout.write(f'{count}\n')

 

다음에는 구글링 없이도 내가 코드를 짤 수 있게 해당 문제는 나의 오답노트 링크로 남겨 놓고 추후 시간이 지나면 다시 풀어봐야 할거 같다

'Python > Python 문제' 카테고리의 다른 글

백준) queuestack  (0) 2024.08.14
백준) 풍선 터뜨리기  (0) 2024.08.14
프로그래머스) 쿼드압축 후 개수 세기  (0) 2024.08.07
백준 ) 게리맨더링  (0) 2024.08.05
백준) 구슬탈출 - 실패  (0) 2024.08.02

문제) 쿼드압축 후 개수 세기

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

주어진 문제로는 0, 1로 이루어진 정 사각형(2**n X 2**n)에서 정사각형으로 나눈 뒤에 해당 영역에 0 또는 1로만 이루어졌다면 압축 하는 방식으로 최종 압축 후의 0의 개수와 1의 개수를 각각 배열에 담아 리턴 해주는 함수를 만드는 문제였다.

 

접근 방식

1. 2의 제곱으로 이루어졌다고 하였고 압축도 2의 제곱이기에 전체 크기를 2로 나누어 가면 될거 같다

2. 파이썬에서 배열을 나눌때에는 a[k:m]형식으로 나눌수 있으나 이는 1차원 배열에만 적용 가능, 2차원 배열에다 동일한 방식으로 나눌려면 numpy 모듈을 사용이 필요(물론 이걸 하드 코딩하는 방식이 있고 그렇게 풀 수도 있지만 numpy를 안다면 괜히 고생할 필요도 없으니 모듈을 사용합시다)

3. 나누어진 곳에서 값들의 개수를 센 후 값들이 전부 동일하면 한개를 제외한 나머지는 -1과 같은 필요 없는 수로 대체

3.1. numpy는 array 형식이기에 개수를 셀려면 형변환이 필요하나 numpy에서 제공하는 함수중 unique를 통하여 갯수를 셀 수이다

3.2 만약 unique를 모른다고하면 list로 형변환 한뒤(numpy.tolist()) count()혹은 for문을 배회하며 를 통하여 문자를 셀 수 있다.

4. 최종적으로 큰 배열에서 작은 배열까지 전부 조회 후 전체 수를 카운트

 

더보기

1 차 코드

import numpy as np
from collections import Counter
def solution(arr):
    answer = []
    k = len(arr) // 2
    while k > 1:
        np_arr = np.array(arr)
        for i in range(0, len(arr), +k):
            for j in range(1, (len(arr) // k) + 1):
                print(np_arr[i: i + k, (j - 1) * k: j * k])
                value, count = np.unique(np_arr[i: i + k, (j - 1) * k: j * k], return_counts=True)
                if len(count) == 1 and value != -1:
                    is_ok = False
                    for x in range(i, i+k):
                        for y in range((j - 1) * k, j * k):
                            if not is_ok:
                                is_ok = True
                                continue
                            arr[x][y] = -1
        k //= 2
    num_counter = Counter(sum(arr, []))
    answer = [num_counter[0], num_counter[1]]
    return answer

해당 코드는 일단 먼저 2로 나누어 진행하는 방식으로 진행하였다.

그런데 이렇게 먼저 2로 나누어 진행하게 될 경우 

[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]의 배열이 주어졌다고 하면 답으로 [0, 1]이 나와야 되는데 [0, 4]가 나오게 된다

그렇기에 처음에는 전체 크기에서 확인이 필요

더보기

최종 코드

import numpy as np
from collections import Counter
def solution(arr):
    answer = []
    k = len(arr)
    while k > 1:
        np_arr = np.array(arr)
        for i in range(0, len(arr), +k):
            for j in range(1, (len(arr) // k) + 1):
                print(np_arr[i: i + k, (j - 1) * k: j * k])
                value, count = np.unique(np_arr[i: i + k, (j - 1) * k: j * k], return_counts=True)
                if len(count) == 1 and value != -1:
                    is_ok = False
                    for x in range(i, i+k):
                        for y in range((j - 1) * k, j * k):
                            if not is_ok:
                                is_ok = True
                                continue
                            arr[x][y] = -1
        k //= 2
    num_counter = Counter(sum(arr, []))
    answer = [num_counter[0], num_counter[1]]
    return answer

 

이렇게 처음 문제를 접했을 때에는 막막해 보였던 문제가 차분히 생각을 정리하면서 코드를 작성하다 보니 무난하게 풀리게 되었다

 

물론 numpy 나 Counter 모듈을 사용하지 않았다면 코드가 길어지기도 하겠지만 다행히 지난 코드카타를 진행하면서 파이썬 문법은 물론 여러가지 모듈을 알게 되어 문제 난이도가 조금은 내려간것 같다

앞으로 꾸준히 문제를 풀어가며 실력을 쌓아야 겠다

목표는 일단 내일배움캠프에서 준 알고리즘 코드카타 정복이며 최종적으로는 프로그래머스 문제(Lv.3 정복) 및 백준 문제(단계 로 되어있는 부분 전부 풀기)를 최대한 많이 풀어볼 생각이다

'Python > Python 문제' 카테고리의 다른 글

백준) 풍선 터뜨리기  (0) 2024.08.14
백준) 골드바흐 파티션  (0) 2024.08.09
백준 ) 게리맨더링  (0) 2024.08.05
백준) 구슬탈출 - 실패  (0) 2024.08.02
백준 ) 알고리즘 수업 - 삽입 정렬 1  (0) 2024.08.02

문제) 게리맨더링

 

문제에서 요구하는 사항은 2개의 선거 구역은 구분 지어 인구수 차가 최대한 적을 때의 값을 찾는 것이다

이때 나누어 진 구역은 서로 연결 되어 있어야 하며 구역은 최소 1개의 구역이 되어 한다

 

위의 조건은 보고 처음 풀이 접근 하기가 조금 어려웠다.

 

이유는 처음 입력을 이해하기가 조금 어려웠다.

 

이제는 해당 입력에 대하여 설명을 할 수 있을 정도가 되었으나 처음 풀이 할때에는 2번째 줄까지만 이해하고 이후 줄을 이해하기까지 조금 시간이 걸렸다.

 

결국은 첫번째 줄은 구역의 수이며 두번째 줄은 각 구역의 인구수를 순차적으로 주어 졌다

그다음 줄은 각 구역의 연결된 구역의 수와 구역의 이름을 주어 진다

 

이를 통하여 기본 적인 코딩을 진행 하면 아래와 같은 코딩이 나온다

import sys
N = int(sys.stdin.readline().strip())
peple_lst = list(map(int, sys.stdin.readline().split())
graph = {}
for i in rnage(1, N + 1):
	info = list(map(int, sys.stdin.readline().split())
	graph[i] = info[1:]

 

이렇게 기초 코딩 을 한 뒤 이제는 각 구역을 조합한 뒤 조합 된 내용을 기준으로 연결 여부 를 확인 하면 된다

그런 뒤 연결 되었다면 두 구역의 인구수의 합 구한 뒤 그 차이를 비교하면 된다

 

파이썬에서 조합은 직접 구현을 하지 않아도 itertools 패키지를 통하여 사용 할 수있다

 

이번 풀이 때에는 조금 어려웠기도 하고 저번 스쿼드 탐방 때 튜터님이 코딩하는 방식이 기억이 남아 내가 무엇을 구현해야 되는 지 글로 적은 뒤 그 글로 적은 부분을 구현을 하였다.

 

# """
# 1. 최소 한개 이상의 구역이 필요
#     1.1 [1], [2, 3, 4, 5, 6, 7] ....
#     1.2 [1, 2], [3, 4, 5, 6, 7] ....
#     1.3 [1, 2 ,3], [4, 5, 6, 7] ....
# --------------------------------------
# 2. 한개 면 다른 남은 부분연 연결 확인
# 3. 한개 이상 이면 연결 되는지 검증
# 4. 최소 값을 찾기에 모든 경우의 수를 찾아 최소 값 찾기
#     4.1 0 이면 바로 반환
# """

 

위의 글 처럼 일단 조합을 먼저 구하고 이후 나누어진 구역들의 수를 체크 하여 1개 이상이면 연결 여부를 확인 하는 코드를 추가하면 된다

 

1번의 조합을 보면 알 듯이 전체 크기N의 (N/2) + 1 만큼만 조회하면 된다

이후 dp를 사용하여 풀이를 진행하기도 했는데 dp를 이용 할 경우 모든 구역이 연결 되어 있는지 확인 불가능 하여 bfs를 통하여 해당 문제를 다시 풀었다.

  • 예시( 1-2, 3-4, 5-6) 이렇게 3개 구역으로 나누어 졌을때 조합을 하면 (1-2, 3-4), (5-6) 이 되고 내가 작성한 dp의 경우 1-2 연결 확인 3-4 연결 확인 되어 통과가 된다.
  • 이를 해결 하기 위해서는 제일 앞에 있는 1로 통하여 모두 연결 되는지 확인 하는 방식이 필요하다.
  • 1-2-3이나 1-2-4 가 되는지를 확인 해야 하는데 dp보다는 bfs나 dfs 방식으로 푸는게 좀 더 쉽게 코딩이 가능하다

 

더보기

정답 코드

import sys
from collections import deque
from itertools import combinations
N = int(sys.stdin.readline().strip())
p_count = list(map(int, sys.stdin.readline().split()))
graph = {}
for i in range(1, N + 1):
    lst = list(map(int, sys.stdin.readline().split()))
    graph[i] = lst[1:]
    
num_list = list(graph.keys())


def bfs(groups, tree):
    peple_sum = 0
    visited = {idx: False for idx in groups}
    dq = deque([groups[0]])
    while dq:
        node = dq.popleft()
        for idx in tree[node]:
            if idx in visited and not visited[idx]:
                visited[idx] = True
                dq.append(idx)

    if False in visited.values():
        peple_sum = -1
    else:
        for idx in groups:
            peple_sum += p_count[idx - 1]

    return peple_sum


min_v = sum(p_count)
for k in range(1, (N // 2) + 1):
    combi = combinations(num_list, k)
    for j in list(combi):
        A = list(j)
        if k > 1:
            check1 = bfs(A, graph)
        else:
            check1 = p_count[A[0] - 1]

        if check1 < 0:
            continue

        B = list(set(num_list) - set(j))
        if len(B) > 1:
            check2 = bfs(B, graph)
        else:
            check2 = p_count[B[0] - 1]
        if check2 < 0:
            continue

        min_v = min(min_v, abs(check2 - check1))
        if min_v == 0:
            break

    if min_v == 0:
        break

if min_v == sum(p_count):
    min_v = -1
sys.stdout.write(f'{min_v}')

백준) 구슬탈출

 

이번 포스팅은 하지 않을 생각이였다. 이유는 내가 답을 찾지 못한 문제여서이다. 그러나 미래의 나를 위하여 문제 접근 방식부터 코드의 각 부분을 해석 한 글을 작성하기로 생각하였다.

 

  1. 문제
    • 구슬 2개(빨강, 파랑) 중 빨강 구슬을 N * M 배열에 출구 'O'에 도달할 수 있으면 1을 아니면 0을 출력
    • 이때 빠져나오는 구슬은 빨강 하나여야 한다
    • 또한 도전 기회는 10회 이며 10회를 초과 하면 탈출 실패로 간주 하여 0을 출력
    • 기울기는 상하좌우만 되며 기울어졌을 때 모든 구슬이 해당 방향으로 이동
    • 구슬은 겹쳐지 않는다
  2. 문제 풀이
    • 먼저 N과 M을 입력 받는 부분 작성
    • 이후 N * M의 배열의 값들을 입력 받아 저장하는 부분 작성
    • 입력받아 저장하는 부분에서 구슬들의 초기 위치를 저장 기록
    • BFS 방식으로 모든 경우의 수를 앞에서 부터 찾기 시작
    • 이후 내용은 코드의 주석에 달아 놓겠다
더보기

실패 코드

import sys
from collections import deque
N, M = map(int, sys.stdin.readline().split())
maps = []
R = []
B = []
for i in range(N):
    map_add = list(map(str, sys.stdin.readline().strip()))
    maps.append(map_add)
    if 'R' in map_add or 'B' in map_add:
        for j, word in enumerate(map_add):
            if word == 'R':
                R = [i, j]
            if word == 'B':
                B = [i, j]

# 구슬의 이동 
def moves(x, y, dx, dy, gole_check):
    nx, ny = x, y
    i = 1
    while True:
        cnx, cny = x + (i * dx), y + (i * dy)
        # 구슬이 맵에서 벗어 나는지와 벽또는 앞의 구슬에 막혔는지 확인
        if cnx < 0 or cnx >= N or cny < 0 or cny >= M:
            break
        elif maps[cnx][cny] in ['#', 'R', 'B']:
            break
        else:
            nx, ny = cnx, cny
            # 구슬이 출구를 통과 했는지 확인
            if maps[nx][ny] == 'O':
                if maps[x][y] == 'R':
                    gole_check[0] = True
                else:
                    gole_check[1] = True
        i += 1

    return nx, ny, gole_check


def bfs(R, B):
	# 구슬이 출구에 도달 했는 확인 하는 함수
    is_gols = [False, False]
    dq = deque([0, R, B])
    
    # 구슬의 초기 위치 기록
    x1, y1 = R
    x2, y2 = B
    dx, dy = [-1, 1, 0, 0], [0, 0, -1, 1]
    while dq:
        count = dq.popleft()
        # 기울기의 획수가 10이상면 실패로 종료
        if count > 10:
        	is_gols = [False, False]
            return
        
        # 구슬이 이동하기에 맵에 그려진 구슬을 일단 자리 비움
        maps[x1][y1] = '.'
        maps[x2][y2] = '.'
        rx, ry = dq.popleft()
        bx, by = dq.popleft()
        # 구슬이 받아 변수 값 부터 이동 하기에 초기 위기 셋팅
        maps[rx][ry] = 'R'
        maps[bx][by] = 'B'

        # if count == 6:
        #     print(count)
        #     print('\n'.join(map(str, maps)))
        for i in range(4):
            # 구슬이 출구로 나가지 않은 상태로 가정하기 구슬의 상태 초기화
            is_gols = [False, False]
            
            # 어떤 구슬부터 움직일지 확인
            if (i == 0 and rx < bx) or (i == 1 and rx > bx) or (i == 2 and ry < by) or (i == 3 and ry > by):
                nrx, nry, is_gols = moves(rx, ry, dx[i], dy[i], is_gols)
                
                # 구슬이 동일 한 위치 이면 일단 먼저 해당 구슬 위치 이동
                if ry == by or rx == bx:
                    maps[rx][ry], maps[nrx][nry] = maps[nrx][nry], maps[rx][ry]
                nbx, nby, is_gols = moves(bx, by, dx[i], dy[i], is_gols)
                
                # 이동 해두었던 위치 초기화
                if ry == by or rx == bx:
                    maps[rx][ry], maps[nrx][nry] = maps[nrx][nry], maps[rx][ry]
                if is_gols[1]: continue
            else:
                nbx, nby, is_gols = moves(bx, by, dx[i], dy[i], is_gols)
                if is_gols[1]: continue
                
                # 구슬이 동일 한 위치 이면 일단 먼저 해당 구슬 위치 이동
                if ry == by or rx == bx:
                    maps[bx][by], maps[nbx][nby] = maps[nbx][nby], maps[bx][by]
                nrx, nry, is_gols = moves(rx, ry, dx[i], dy[i], is_gols)
                
                # 이동 해두었던 위치 초기화
                if ry == by or rx == bx:
                    maps[bx][by], maps[nbx][nby] = maps[nbx][nby], maps[bx][by]

			# 빨강 구슬만 출구를 이동했다면 함수 종료
            if is_gols == [True, False]:
                return is_gols

			# 파랑 구슬이 출구를 밟았다면 이후 검색은 필요없기에 다음 작업 수행
            if is_gols[1]:
                continue
			
            # 구슬이 이동이 되었다면 큐에 기록
            if rx != nrx or ry != nry or bx != nbx or by != nby:
                dq.append(count + 1)
                dq.append([nrx, nry])
                dq.append([nbx, nby])

        # 기존의 위치를 초기화 한 뒤 최초의 상태로 구슬 위치 변경
        maps[rx][ry] = '.'
        maps[bx][by] = '.'

        maps[x1][y1] = 'R'
        maps[x2][y2] = 'B'
    return is_gols


check = bfs(R, B)
if check == [True, False]:
    sys.stdout.write(f'{1}')
else:
    sys.stdout.write(f'{0}')

 

 

이 문제는 스쿼드방에서 코드리뷰를 진행한다고 하여 일단 실패 한 상태로 둔 후 코드리뷰 후 다시 코드를 작성 하여 통과 해볼 생각이다.

지금으로써는 어디서, 왜 틀렸는지 조차도 이해가 안되는 상황이라 더는 붙잡고 풀 수는 없을거 같다..

'Python > Python 문제' 카테고리의 다른 글

프로그래머스) 쿼드압축 후 개수 세기  (0) 2024.08.07
백준 ) 게리맨더링  (0) 2024.08.05
백준 ) 알고리즘 수업 - 삽입 정렬 1  (0) 2024.08.02
백준) 유사 라임 게임  (0) 2024.08.01
백준) 분해합  (1) 2024.07.25

문제) 알고리즘 수업 - 삽입 정렬 1

 

해당 문제는 문제의 이름에서 나와 있듯이 삽입 정렬을 구현하여 진행하는 문제이다

문제에서 대강의 로직을 주어져 있기에 그것을 기반으로 문제를 풀어 나가면 되는 문제였다.

 

그런데 쉽게 풀릴 이문제는 생각 보다 많이 틀리게 되었는데 이번 포스팅은 왜 이렇게까지 많이 틀렸는지 에 대한 내용을 다룰려고 한다.

 

 

위의 사진 처럼 10번 째에 맞추게 되었는데 맞춘 부분도 PyPy3에서는 통과가 되고 Python 3에는 시간 초과가 났다. print() 함수를 사용해서 안되는가 해서 sys.stdout.write()도 쓰고 중간 중간 return 해주는 종료 문들도 넣어 주었는데도 시간 초과가 난 것을보아 코딩 로직의 문제보다는 성능 문제 때문인거 같다

 

더보기

실패 1

import sys
N, K = map(int, sys.stdin.readline().split())
a = list(map(int, sys.stdin.readline().split()))
def insertion_sort(A, k): # A[1..N]을 오름차순 정렬한다.
    answer = 3
    v = 0
    for i in range(1, N):
        loc = i - 1
        newItem = A[i]
        while (0 <= loc and newItem < A[loc]):
            A[loc + 1] = A[loc]
            answer += 1
            if answer == k-3:
                v = A[loc + 1]
            loc -= 1
        A[loc + 1] = newItem
    return answer, v
check, v = insertion_sort(a, K)
if check >= K:
    print(v)
else:
    print(-1)

위 코드는 어찌되었든 예시만 출력 해 보자라는 마인드로 어거지로 만든 부분이다. 코드를 보시면 아시겠지만 answer 값을 초기에 3을 추가 해주고 돌린 부분이다.

지금 생각해보면 왜 저렇게까지 했는지 아직도 의아한 부분이다....(많이 힘들었나? 뭐, 백준 구슬탈출을 계속 실패만 머리도 식힐겸 푼 부분이라...)

 

어찌 되었든 위의 코드는 말그대로 예시만 출력 해주는 부분이기에 당연히 다른 반례가 들어가면 정상 동작이 안되는 부분이다.

그렇다면 어떻게 코드를 풀어야 할까?

더보기

실패 코드 2

import sys
N, K = map(int, sys.stdin.readline().split())
a = list(map(int, sys.stdin.readline().split()))
def insertion_sort(A, k): # A[1..N]을 오름차순 정렬한다.
    answer = -1
    v = 0
    for i in range(1, N):
        loc = i - 1
        newItem = A[i]
        answer += 1
        if answer == k:
            v = A[loc + 1]
        while (0 <= loc and newItem < A[loc]):
            A[loc + 1] = A[loc]
            answer += 1
            if answer == k:
                v = A[i]
            loc -= 1
        A[loc + 1] = newItem
    return answer, v

check, v = insertion_sort(a, K)
if check >= K:
    print(v)
else:
    print(-1)

일단 초기값 answer를 원래대로 돌리는게 맞다. 임의 값을 주는게 아닌 시작값을 고정으로 해야 맞고 이후 정렬 작업시 answer 값을 컨트롤 하는게 맞다

 

그런데 이 코드도 자세히 보면 answer 값이 -1로 되어있다. 이 부분은 내가 생각한 값보다 1이 더 출력이 되기에 시작 값을 -1로 했는데 그러면 안된다. 이렇게 푸는 건 첫번째로 풀었던 예시만 정답이다라는 생각을 가지고 푼거랑 크게 다를바가 없기 때문이다

그러므로 answer 의 값은 0 이 되어야 한다.

 

자 그럼 이제 해결 방법들이 거의다 나왔다.

1. answer의 값은 0으로 시작

2. answer 값이 K일때 값을 찾아 출력 해주기

3. answer 값이 K 보다 작으면 -1을 출력 해주기

 

더보기

실패 코드 3

import sys
N, K = map(int, sys.stdin.readline().split())
a = list(map(int, sys.stdin.readline().split()))
def insertion_sort(A, k): # A[1..N]을 오름차순 정렬한다.
    answer = 0
    v = 0
    for i in range(2, N):
        loc = i - 1
        newItem = A[i]
        while 0 <= loc and newItem < A[loc]:
            A[loc + 1] = A[loc]
            answer += 1

            if answer == k:
                v = A[loc + 1]
            loc -= 1

        if loc + 1 != i:
            A[loc + 1] = newItem
        answer += 1
        if answer == k:
            v = A[loc + 1]
    return answer, v

check, v = insertion_sort(a, K)
if check < K:
    print(-1)
else:
    print(v)

그렇게 해서 문제에서 요구한 사항 대로 거의다 만들었다. 그런데 이때 나는 큰 실수를 했다. 그건 문제에서 2부터 시작 한다는 부분에서 착각을 하여 for 문 시작 값으 2로 준것이다

그리고 for문 마지막의 answer 증가 부분을 무조건으로 했던 부분도 있었다.

 

더보기

최종! 정답 주의 !!!

import sys
N, K = map(int, sys.stdin.readline().split())
a = list(map(int, sys.stdin.readline().split()))
def insertion_sort(A, k): # A[1..N]을 오름차순 정렬한다.
    answer = 0
    v = 0
    for i in range(1, N):
        loc = i - 1
        newItem = A[i]
        while 0 <= loc and newItem < A[loc]:
            A[loc + 1] = A[loc]
            answer += 1

            if answer == k:
                v = A[loc + 1]
            loc -= 1

        if loc + 1 != i:
            A[loc + 1] = newItem
            answer += 1
            if answer == k:
                v = A[loc + 1]
    return answer, v

check, v = insertion_sort(a, K)
if check < K:
    print(-1)
else:
    print(v)

그렇게 실수들을 바로 잡고 코드를 작성하니 무사히 통과를 하게 되었다.

 

이번에 문제를 통하여 느낀점은 너무 내 생각에 빠져 무조건 그 코드만 가지고 풀려고 하는 부분이 컸던거 같다.

때문 처음으로 돌아가 새로운 방식으로 접근도 해보고 왜 내가 작성 한 코드는 초기 값을 0이 아닌 다른 것으로 주고 해야지만 맞는 건지에 대해 깊게 고민해 보는 습관을 길러야 할 거같다

초기값을 임의로 문제에 정답이 나오도록 셋팅하는 건 틀린 코드이다.

물론 틀린 코드 또한 어찌 되었든 만드는게 중요하다. 우리는 무에서 유를 창조를 할 수 있는 능력자가 아니다.

그러니 아예 손도 못대기 보다는 어떻게든 손을 대고 그 손댄 부분에서 어떻게 하면 돌아 갈지 라는 생각까지 해내는게 무엇보다 중요하다고 나는 생각한다

 

이글은 누군가 읽어 보게 된다면 어찌되었든 포기는 하지말고 끝까지 엉덩이 붙이고 해결 해 나가려는 끈기를 가지기를 바라며 글을 마치겠다.

'Python > Python 문제' 카테고리의 다른 글

백준 ) 게리맨더링  (0) 2024.08.05
백준) 구슬탈출 - 실패  (0) 2024.08.02
백준) 유사 라임 게임  (0) 2024.08.01
백준) 분해합  (1) 2024.07.25
백준) 블록 놀이  (0) 2024.07.24

+ Recent posts