3 minute read

1. 직각삼각형

  • 과거 이집트인들은 각 변들의 길이가 3, 4, 5인 삼각형이 직각 삼각형인것을 알아냈다.
  • 주어진 세변의 길이로 삼각형이 직각인지 아닌지 구분하시오.

입력

  • 입력은 여러개의 테스트케이스로 주어지며 마지막줄에는 0 0 0이 입력된다.
  • 각 테스트케이스는 모두 30,000보다 작은 양의 정수로 주어지며, 각 입력은 변의 길이를 의미한다.

출력

  • 각 입력에 대해 직각 삼각형이 맞다면 “right”, 아니라면 “wrong”을 출력한다.

풀이

import sys

while True:
    a,b,c = list(map(int,sys.stdin.readline().split()))
    if a == 0 and b == 0 and c == 0: break
    print("wrong" if max(a,b,c) ** 2 != sum([x**2 for x in [a,b,c] if x != max(a,b,c)]) else "right")

채점 결과

  • 정답

리뷰

  • 피타고라스 정리를 활용한다.

  • 가장 긴 변을 찾아 제곱하고, 나머지 두개를 제곱한 값과 비교하는 방식으로 풀었다.

2. 체스판 다시 칠하기

  • 지민이는 자신의 저택에서 MN개의 단위 정사각형으로 나누어져 있는 M×N 크기의 보드를 찾았다. 어떤 정사각형은 검은색으로 칠해져 있고, 나머지는 흰색으로 칠해져 있다. 지민이는 이 보드를 잘라서 8×8 크기의 체스판으로 만들려고 한다.

  • 체스판은 검은색과 흰색이 번갈아서 칠해져 있어야 한다. 구체적으로, 각 칸이 검은색과 흰색 중 하나로 색칠되어 있고, 변을 공유하는 두 개의 사각형은 다른 색으로 칠해져 있어야 한다. 따라서 이 정의를 따르면 체스판을 색칠하는 경우는 두 가지뿐이다. 하나는 맨 왼쪽 위 칸이 흰색인 경우, 하나는 검은색인 경우이다.

  • 보드가 체스판처럼 칠해져 있다는 보장이 없어서, 지민이는 8×8 크기의 체스판으로 잘라낸 후에 몇 개의 정사각형을 다시 칠해야겠다고 생각했다. 당연히 8*8 크기는 아무데서나 골라도 된다. 지민이가 다시 칠해야 하는 정사각형의 최소 개수를 구하는 프로그램을 작성하시오.

입력

  • 첫째 줄에 N과 M이 주어진다. N과 M은 8보다 크거나 같고, 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 보드의 각 행의 상태가 주어진다. B는 검은색이며, W는 흰색이다.

출력

  • 첫째 줄에 지민이가 다시 칠해야 하는 정사각형 개수의 최솟값을 출력한다.

풀이


import sys

N, M = list(map(int, sys.stdin.readline().split()))

board = [[-1 for x in range(0, M)] for x in range(0, N)]
sample_board_0 = [["WBWBWBWB"], ["BWBWBWBW"]] * 4
sample_board_1 = [["BWBWBWBW"], ["WBWBWBWB"]] * 4

for i in range(0, N):
    board[i] = sys.stdin.readline().strip()

min_ = 65
for n in range(0, N - 7):
    for m in range(0, M - 7):
        cnt0 = 0
        cnt1 = 0
        for i in range(n, n + 8):
            for j in range(m, m + 8):
                if board[i][j] != sample_board_0[i - n][0][j - m]:
                    cnt0 += 1
                if board[i][j] != sample_board_1[i - n][0][j - m]:
                    cnt1 += 1

        cnt = min(cnt0, cnt1)
        if cnt < min_:
            min_ = cnt

print(min_)


채점 결과

  • 정답

리뷰

  • 이중반복문을 통한 탐색으로 두 타입의 보드중 바꿔야할 사각형 개수가 적은것을 채택한다.

더 나은 풀이

import sys
from itertools import accumulate as acc
input = sys.stdin.readline
n, m = map(int, input().split())
y = [[0]*(m+1)]
for i in range(n):
    ac = [0]
    ac.extend(acc([((s=='W')+i+j) % 2 for j, s in enumerate(input().rstrip())]))
    y.append([k + j for k, j in zip(ac, y[-1])])

res = 32
for i in range(n-7):
    for j in range(m-7):
        u = y[i+8][j+8]-y[i+8][j]-y[i][j+8]+y[i][j]
        res = min(res, u, 64-u)
print(res)

더 나은 풀이 리뷰

  • itertools.accumulate

    • 누적 합계나 다른 이항 함수(선택적 func 인자를 통해 지정됩니다)의 누적 결과를 반환하는 이터레이터를 만듭니다.

    • func가 제공되면, 두 인자를 취하는 함수여야 합니다. 입력 iterable의 요소는 func에 대한 인자로 허용될 수 있는 모든 형일 수 있습니다. (예를 들어, 기본 더하기 연산에서 요소는 Decimal이나 Fraction을 포함하는 모든 더할 수 있는 형일 수 있습니다.)

    • 출처 : https://docs.python.org/ko/3/library/itertools.html#itertools.accumulate

    • 반복합을 뽑아내는 함수인데, for문으로 뽑아내는것에 비해 압도적인 성능을 지닌다.(성능비교 관련 블로그 정리글)

  • enumerate(iterable)

    • (index, iter)로 출력.
  • [((s=='W')+i+j) % 2 for j, s in enumerate(input().rstrip())]는 입력받은 값이
    • 홀수줄에서는 “WBWB…“로 이루어진 체스판의 줄
    • 짝수줄에서는 “BWBW…“로 이루어진 체스판의 줄
    • 에 대해 일치하는 인덱스에 1, 불일치시 0을 저장한 배열을 return한다.
  • ac는 위의 배열에 대해 반복합을 구한것이므로, 체스판과 다른 모양이 있는 구간이 있는 경우 그 부분부터 1 증가한 값을 가지는 배열을 return한다.

    • ex) WBWBWBWB => 0,1,2,3,4,5,6,7,8
    • ex) WBWBBBWB => 0,1,2,3,4,4,5,6,7
  • 즉, ac의 누적합인 y는 다음과 같은 사항이 적용된다.
    • WBWB..로 시작하는 체스판에 대해서 완전 일치할때 64
      • 틀린 부분이 발생시 63,62,..로 감소
    • BWBW..로 시작하는 체스판에 대해서 완전 일치할때 0
      • 틀린 부분이 발생시 1,2,..로 증가
  • y[i+8][j+8]은 i~i+8까지, j~j+8까지의 체스판이 가지는 누적 오차를 의미한다.
    • 이때 y[i+8][j]와 y[i][j+8]를 빼는 이유는 i,j이전 열과 행들의 오차에 영향을 받지 않기 위함이다.
    • 단, y[i+8][j]와 y[i][j+8]을 두번 뺌으로 인해 중복해서 제거된 영역인 y[i][j]를 더해주어야 한다.
  • res는 해당 체스판에서 가장 적은 오차를 의미하며, 전부 바꿔야하는 경우(32)를 기본값으로 모든 판을 순회하며 누적 오차중 가장 적은 값을 저장한다.

  • 내가 봐온 모든 정답자 코드중에서 가장 해석하기 난해한 코드였던것 같았다.

    • 코드 해석을 진행하며 계속 수정을 덧붙여서 표가 안나겠지만, 이 코드 해석에만 3~4일 정도 사용한것 같다.

    • 언젠간 나도 저런 코드를 짤수 있을 날이 오려나, 다시한번 공부의 부족함을 실감한 며칠이었다.