2차원 리스트

관련 내용

리스트

시뮬레이션

2차원 리스트

우리는 그동안 리스트 안에 문자열이나 숫자를 넣어서 문제를 푸는 1차원 리스트를 사용해왔습니다. 이번 시간에는 리스트 안에 리스트가 들어가는 2차원 리스트에 대해 배워보겠습니다.

이해하기 쉽도록 1차원 리스트와 비교를 하면서 글을 썼으니 아직 리스트를 잘 모르시는 분은 2주차 - 리스트 파트를 참고해주시길 바랍니다.

2차원 리스트 표현

우리가 그동안 사용해왔던 1차원 리스트를 생각해봅시다.

arr = [1, 2, 3, 'abc', 'cat']

위와 같이 요소의 값들이 전부 숫자 아니면 문자열이라는 것을 알 수 있습니다.

2차원 리스트는 1차원 리스트 여러 개를 한 묶음으로 묶기 위해 바깥에 대괄호 [ ]를 감싸준 것으로 생각하면 쉽습니다. 그래서 2차원 리스트의 구조는 겉에 있는 리스트는 여러 개의 리스트를 요소로 가지고 있고, 안에 있는 리스트들은 우리가 배운 1차원 리스트처럼 숫자와 문자열을 요소로 가지고 있습니다.

글로만 보면 이해가 잘 안되니까 바로 예시를 들어서 설명하겠습니다.

arr1 = [1, 2, 3]
arr2 = [4, 5, 6]
arr3 = [7, 8, 9]

위와 같이 3개의 리스트가 있는데요. 저 리스트들을 한 곳에 뭉쳐서 arr이라는 리스트로 묶으려고 합니다. 그러면 위에서 언급한 내용대로 대괄호 [ ] 를 사용해주면 됩니다.

arr1 = [1, 2, 3]
arr2 = [4, 5, 6]
arr3 = [7, 8, 9]


arr = [arr1, arr2, arr3]

아니면 변수명을 사용하지 않고 리스트를 그대로 넣어도 됩니다.

arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

이렇게 겉에 있는 리스트는 [ [], [], [] ] 로 3개의 리스트를 요소로 가지고 있습니다.

그리고 안에 있는 리스트들은 각각 1부터 3, 4부터 6, 7부터 9 값을 가진 1차원 리스트입니다.

리스트에 리스트를 넣고 빼는 방법

리스트에 리스트를 넣고 빼는 방법도 간단합니다. 리스트 안에 문자열이나 숫자를 넣는 것처럼 똑같이 append()와 pop() 메소드를 이용하여 넣고 빼주면 됩니다.

arr = []
arr1 = [1, 2, 3]
arr2 = [4, 5, 6]
arr3 = [7, 8, 9]

arr.append(arr1)
arr.append(arr2)
arr.append(arr3)

print(arr)
# [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

arr.pop()
print(arr)
# [[1, 2, 3], [4, 5, 6]]

print(arr.pop())
# [4, 5, 6]

2차원 리스트 입력 받기

우리가 1차원 리스트를 입력 받을 때는 아래와 같이 입력을 받았습니다.

arr = list(map(int,input().split()))

arr = input().split()

2차원 리스트는 1차원 리스트를 입력 받아서 리스트 안에 추가해주면 됩니다.

arr = []
for i in range(10):
    arr_x = list(map(int,input().split()))
    arr.append(arr_x)
arr = []
for i in range(10):
    arr.append(list(map(int,input().split())))

위와 같이 append() 메서드를 이용하여 리스트 안에 리스트를 넣거나, 아래처럼 아예 한 줄로 작성할 수도 있습니다.

arr = list(list(map(int,input().split())) for _ in range(10))

arr = [list(map(int,input().split())) for _ in range(10)]

2차원 리스트의 인덱싱

arr = [1, 2, 3, 4, 5]

위와 같은 1차원 리스트가 있을 때 값 4에 접근하는 방법은 무엇일까요?

arr = [1, 2, 3, 4, 5]

print(arr[3])

바로 이렇게 arr[3]으로 인덱싱하여 출력을 할 수 있음을 알 수 있습니다.

2차원 리스트의 인덱싱은 1차원 리스트와 비슷합니다. 하지만 차원이 하나 늘어났기 때문에 몇 번째 리스트에서 값을 뽑아내야 하는지도 입력해야 합니다.

2차원 리스트의 인덱싱은 1차원 리스트와 마찬가지로 0번째부터 시작합니다. 그리고 위에 나온 그림처럼 처음 대괄호 값은 몇 번째 리스트인지 입력을 해야 하고, 두 번째 대괄호 값은 arr[x] 리스트의 몇 번째 요소의 값이 필요한지 입력을 하면 됩니다.

이제 예시를 통해 살펴보겠습니다.

아래와 같은 배열이 있다고 할 때, 여기서 인덱싱을 이용하여 값 6을 출력하는 방법은 무엇일까요?

arr = [[1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]]

arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

값 6은 리스트의 두 번째 리스트에 있으므로 x = 1이고, arr[1]에서 6의 값은 세 번째 값이므로 y = 2입니다. 따라서 arr[1][2]를 출력하면 되겠습니다.

arr = [[1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]]

arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

print(arr[1][2]) # 6

다른 예시를 살펴봅시다.

arr = [['A+', 'A0'], ['B+', 'B0'], ['C+', 'C0'], ['D+', 'D0', 'F']]

위와 같은 2차원 리스트가 있을 때, 여기서 인덱싱을 이용하여 F를 출력하려고 합니다.

F는 일단 4번째 리스트에 있으므로 x = 3이고, 4번째 리스트의 3번째 값에 있으니 y = 2로 arr[3][2]를 출력하면 되겠습니다.

arr = [['A+', 'A0'], ['B+', 'B0'], ['C+', 'C0'], ['D+', 'D0', 'F']]

print(arr[3][2])

좀 더 어려운 예시를 풀어봅시다.

59 54 45 2 1
51 67 65 86 97
36 27 2 51 50
60 16 37 56 97
60 3 52 17 92

이번엔 이런 입력 값이 주어질 때, 30 이상의 값을 출력해봅시다.

일단 1차원 리스트로 풀어봅시다.

for i in range(5):
    arr = list(map(int,input().split()))
    for j in range(5):
        if arr[j] >= 30: print(arr[j]

이렇게 for문을 5번 돌리고, arr이라는 리스트에 값을 저장한 다음, 다시 for문을 이용하여 arr[j] >= 30일 때 출력하면 되겠습니다.

이번엔 2차원 리스트로 풀어보겠습니다.

일단 입력값을 받아서 2차원 리스트로 저장을 해줘야 합니다.

# 방법1
arr = list(list(map(int,input().split())) for _ in range(5)

# 방법2
arr = []
for i in range(5):
    arr.append(list(map(int,input().split())))

위에 2가지 방법중에 아무거나 편한 방법을 사용하면 됩니다. 여기서는 방법 1을 사용하겠습니다.

이제 입력 값을 받았으면 2중 for문을 돌려서 arr[x][y]의 x와 y값을 정해야 합니다.

여기서 x는 1차원 리스트의 개수이니 len(arr)이 될 수 있고, y는 1차원 리스트의 요소 개수를 구해야 하므로 len(arr[x])가 될 수 있습니다.

arr = list(list(map(int,input().split())) for _ in range(5)

for i in range(len(arr)):
    for j in range(len(arr[i])):
        blah blah

이제 arr[x][y] 값을 하나하나 볼 수 있으니 조건문을 추가하여 출력해주면 됩니다.

arr = list(list(map(int,input().split())) for _ in range(5)

for i in range(len(arr)):
    for j in range(len(arr[i])):
        if arr[i][j] >= 30: print(30)

그 외

2차원 리스트도 1차원 리스트처럼 리스트의 슬라이싱, 연산 역시 지원해줍니다.

이 부분은 문제에서 자주 사용하지 않으므로 생략하겠습니다. 궁금하신 분들은 2차원 리스트를 만들어서 직접 사용해보시길 바랍니다.

Tip

2차원 배열의 문제는 보통 좌표 평면이 떠오르게 하는 문제들이 많이 출제되는 편입니다

59 54 45 2 1
51 67 65 86 97
36 27 2 51 50
60 16 37 56 97
60 3 52 17 92

위에서 사용했던 예시를 가지고 왔는데요. 이것을 좌표 평면으로 그려보면 아래와 같습니다.

여기서 첫 번째 줄 처음 값인 59를 인덱싱으로 접근하면 arr[0][0]이 될 것이고, 마지막 줄 마지막 값인 92를 인덱싱으로 접근하면 arr[4][4]가 되겠습니다.

이 값들을 하나하나 (x, y)꼴로 표현하면 아래 사진처럼 나옵니다.

이렇게 위에서 아래로 이동하면 x의 값이 증가하고, 왼쪽에서 오른쪽으로 이동하면 y의 값이 증가하는 것을 알 수 있습니다.

이것을 우리가 수학 시간에서 배웠던 좌표 평면과 비슷하게 생각할 수 있습니다. 수학 시간에서 배우는 좌표 평면은 (x, y)로 표현을 할 때, 2차원 리스트의 경우에는 (y, x)로 해석할 수 있습니다.

그리고 좌표 평면은 위로 갈수록 y의 값이 증가하지만 2차원 리스트의 경우에는 아래로 갈수록 y의 값이 증가한다고 생각하시면 됩니다.

선형대수학에서 배우는 행렬을 아시는 분이면 좀 더 쉽게 이해할 수 있습니다.

행렬은 2차원 리스트와 똑같이 작동합니다. 다만 2차원 리스트를 행렬로 생각한다면 첫 번째 행, 첫 번째 열로 시작하는 것이 아니라 0번째 행, 0번째 열부터 시작한다고 생각하시면 이해하기 편합니다.

연습 문제

2차원 리스트의 문제는 처음에 접했을 때 난이도가 높고, 리스트를 활용하는 방법도 다양하기 때문에 처음 접했을 때는 많이 어려운 편입니다. 그래서 이번 파트에서는 4문제를 같이 풀겠습니다.

BOJ 1100

8 * 8의 체스판이 주어지고 가장 왼쪽 칸인 (0, 0)의 색깔이 하얀색일 때, 하얀 칸에 말이 몇 개 들어가 있는지 구하는 문제입니다.

이 문제는 2중 for문으로 arr[x][y]를 인덱싱하여 하얀 칸에서 F가 들어있는 개수를 구하면 될 것 같습니다.

일단 입력값을 받는 코드를 짜봅시다.

# 방법1
arr = [list(input()) for _ in range(8)]

# 방법2
arr = []
for i in range(8):
    arr.append(list(input()))

방법은 자유롭게 하시면 됩니다. 저는 방법 2를 사용하겠습니다.

arr = []
for i in range(8):
    arr.append(list(input()))

ans = 0

이제 arr[i][j] == 'F'인지 확인하는 코드를 2중 for문으로 만들어봅시다.

arr = []
for i in range(8):
    arr.append(list(input()))

ans = 0
for i in range(8):
    for j in range(8):
        if arr[i][j] == 'F': ans += 1

그런데 문제에서 하얀 칸일 때만 말이 몇 개 있는지 구하는 것이므로 color 변수를 만들어서 하얀색일 때만 ans에 더할지 생각해봅시다.

체스판 일부를 그려보면 아래 그림과 같습니다.

처음 줄에는 흰색이 시작하고, 두 번째 줄은 검은색, 세 번째 줄은 흰색, ... 이런 식으로 반복하겠네요.

2중 for문에서 처음 for문이 될 때 i의 값이 0, 1, 2, ...이므로 color = i % 2로 설정하면 첫 번째 줄은 color = 0, 두 번째 줄은 color = 1, 세 번째 줄은 color = 0, ... 이렇게 홀수 줄과 짝수 줄일 때 color의 값이 다르다는 것을 알 수 있습니다.

그러면 자동으로 color = 0이면 흰색, color = 1이면 검은색으로 설정되었네요.

arr = []
for i in range(8):
    arr.append(list(input()))

ans = 0
for i in range(8):
    color = i % 2
    for j in range(8):
        if color == 0 and arr[i][j] == 'F':
            ans += 1

이제 오른쪽으로 이동할 때마다 color의 값을 0 -> 1 -> 0 -> 1 -> 0 -> ... 으로 반복해야 하므로 2중 for문 안에서 color = (color + 1) % 2를 해줍시다. 이러면 1을 계속 더해도 2로 나눈 나머지만 color에 저장되기 때문에 color의 값은 0 아니면 1이 됩니다.

arr = []
for i in range(8):
    arr.append(list(input()))

ans = 0
for i in range(8):
    color = i % 2
    for j in range(8):
        if color == 0 and arr[i][j] == 'F':
            ans += 1
        color = (color + 1) % 2

이제 ans를 출력하면 되겠네요.

arr = []
for i in range(8):
    arr.append(list(input()))

ans = 0
for i in range(8):
    color = i % 2
    for j in range(8):
        if color == 0 and arr[i][j] == 'F':
            ans += 1
        color = (color + 1) % 2
print(ans)

참고로 흰색 칸과 검은색 칸은 어떤 규칙을 가지고 있습니다.

이렇게 좌표를 넣어보면 (x, y)일 때 x+y가 짝수면 하얀 칸, x+y가 홀수면 검은 칸인 것을 알 수 있습니다.

그래서 color의 값을 color = (i + j) % 2로 설정할 수 있습니다.

arr = []
for i in range(8):
    arr.append(list(input()))

ans = 0
for i in range(8):
    for j in range(8):
        color = (i + j) % 2
        if color == 0 and arr[i][j] == 'F':
            ans += 1
print(ans)

BOJ 10864

각 학생의 친구가 몇 명인지 출력하라는 문제입니다.

이 문제는 딕셔너리나 1차원 리스트로 풀 수 있지만 2차원 리스트로 문제를 풀어보겠습니다.

먼저 입력값을 받아봅시다.

n, m = map(int,input().split())
for i in range(m):
    a, b = map(int,input().split())

이렇게 입력 값을 받을 수 있습니다.

먼저 a가 b와 친구 관계이므로 a에 b를 저장하고, b에 a를 저장하는 리스트가 있어야 합니다. 그래서 이걸 friend라는 리스트로 만들어주고, friend안에 학생 n명의 친구 관계가 들어있으니 friend리스트 안에 n개의 리스트를 만들어줘야 합니다.

n, m = map(int,input().split())

friend = [[] for _ in range(n)]

for i in range(m):
    a, b = map(int,input().split())

그런데 이 friend라는 리스트는 만약 n = 5라도 인덱싱은 friend[4]까지만 있으므로 a와 b를 입력 받으면 1씩 빼주거나, friend리스트의 길이를 n+1로 설정합시다.

# 방법 1
n, m = map(int,input().split())

friend = [[] for _ in range(n)]

for i in range(m):
    a, b = map(int,input().split())
    a -= 1
    b -= 1

# 방법 2
n, m = map(int,input().split())

friend = [[] for _ in range(n+1)]

for i in range(m):
    a, b = map(int,input().split())

저는 주로 방법 1로 문제를 풀고 있으므로 방법 1로 진행하겠습니다.

이제 friend[a]에 b를 추가하고, friend[b]에 a를 추가해주면 됩니다.

n, m = map(int,input().split())

friend = [[] for _ in range(n)]

for i in range(m):
    a, b = map(int,input().split())
    a -= 1
    b -= 1
    friend[a].append(b)
    friend[b].append(a)

이제 친구를 다 추가했으면 for문을 이용하여 len(friend[i])를 통해 i번 학생의 친구 수를 구할 수 있게 됩니다.

n, m = map(int,input().split())

friend = [[] for _ in range(n)]

for i in range(m):
    a, b = map(int,input().split())
    a -= 1
    b -= 1
    friend[a].append(b)
    friend[b].append(a)

for i in range(n):
    print(len(friend[i]))

BOJ 2999

상근이가 받은 이메일을 해독하는 문제입니다.

여기서 행은 세로를 뜻하고, 열은 가로를 뜻합니다. 그래서 세로 길이인 R이 가로 길이인 C보다 작거나 같아야 하고(R <= C), R * C = N을 만족하는 R이 여러 개 있을 때 최댓값을 R로 선택하라고 합니다.

R과 C를 어떻게 구해야 하는지 생각해보면 약수 개념이 필요한 것을 알 수 있습니다. N의 약수 중에서 R과 C를 선택해야겠네요. 그래서 약수를 구하는 코드를 구현하는 것이 필요하겠습니다.

일단 입력값을 받는 코드를 짜봅시다. 여기서 N은 입력값의 길이를 뜻하니까 n = len(s)도 같이 넣어줍시다.

s = input()
n = len(s)

이제 n의 약수를 구하는 코드를 짜봅시다. 약수는 만약 24의 약수는 1, 2, 3, 4, 6, 8, 12, 24이므로 for문을 돌리면 되겠네요. 약수는 전부 24에 나눌 때 나머지가 0이므로 조건문을 추가해서 약수를 구해주시면 됩니다.

s = input()
n = len(s)

divisor = []
for i in range(1, n+1):
    if n % i == 0: divisor.append(i)

이제 약수를 구한 리스트에서 R이 될 수 있는 가장 큰 수를 구해줘야 하는데, 이것은 약수의 길이가 짝수일 때, 예를 들자면 n = 24일 때 1, 2, 3, 4, 6, 8, 12, 24에서 숫자를 1, 2, 3, 4와 6, 8, 12, 24로 분리하면 1, 2, 3, 4에서 가장 큰 값인 4가 될 수 있고, 약수의 길이가 홀수일 때, 예를 들자면 n = 25일 때는 약수가 1, 5, 25에서 가운데 값인 5가 될 수 있습니다.

즉 divisor의 길이가 짝수이면 R이 될 수 있는 가장 큰 수는 divisor[len(divisor)//2 - 1]일 것이고, 홀수이면 R이 될 수 있는 가장 큰 수는 divisor[len(divisor)//2]가 되겠습니다.

왜 이렇게 되는지 잘 이해를 못하겠다면 n에다가 다른 숫자를 넣어서 적용해보시길 바랍니다.

s = input()
n = len(s)

divisor = []
for i in range(1, n+1):
    if n % i == 0: divisor.append(i)

if len(divisor) % 2 == 0:
    r = divisor[len(divisor)//2 - 1]
    c = n // r
else:
    r = divisor[len(divisor)//2]
    c = n // r

이제 r과 c를 구했으니 r * c 크기인 2차원 리스트를 만들어주고 s에 있는 문자를 하나하나 선택하여 2차원 리스트에 넣어줍시다.

먼저 r * c 크기인 2차원 리스트를 만들어주려면 가로 길이가 c이므로 리스트를 하나 만들어서 [' ']*c인 1차원 리스트를 r개를 넣어주면 됩니다.

s = input()
n = len(s)

divisor = []
for i in range(1, n+1):
    if n % i == 0: divisor.append(i)

if len(divisor) % 2 == 0:
    r = divisor[len(divisor)//2 - 1]
    c = n // r
else:
    r = divisor[len(divisor)//2]
    c = n // r


# 방법 1
arr = [[' ']*c for _ in range(r)]

# 방법 2
arr = []
for i in range(r):
    arr.append([' ']*c)

저는 방법1로 진행하겠습니다.

이제 s에 있는 문자를 2차원 리스트인 arr에 넣어야 하는데요. 아래 힌트를 보니까 문자열을 입력받아서 세로로 값을 넣고 있습니다.

이걸 좌표로 표현하게 되면 위 사진처럼 나옵니다. 그리고 문자열을 넣는 방향을 그림 그려보면 아래와 같습니다.

이것을 2중 for문으로 구현해야 하는데요. 위 방식은 (x, y)로 치면 x값이 먼저 증가하고 그다음 y값이 증가하므로 (0, 0) -> (1, 0) -> (2, 0) -> (3, 0) -> (0, 1) -> (0, 2) -> .... 로 움직이므로 2중 for문을 다음과 같이 구현할 수 있습니다.

for y in range(c): # (x,y)에서 x값을 다 움직여야 y값이 1 증가하므로 
    for x in range(r): # y값이 1 증가하기 전에 x값을 다 훑어야 하므로
        arr[x][y] = ???

이제 s를 인덱싱해서 arr[x][y]에 값을 추가해야 합니다.

예시로 s = 'abcdef'라고 해보겠습니다. 그러면 6의 약수는 1, 2, 3, 6이므로 r = 2, c = 3이 되겠네요.

위 문자를 2차원 리스트에 넣어줘야 하는데요. 먼저 2 * 3 리스트에 넣기 전에 먼저 r = 3, c= 2인 리스트로 문제를 생각해봅시다.

s = 'abcdef'를 오른쪽 리스트에 넣는 방법은 어떻게 하면 될까요?(r = 3, c = 2)

  • 첫 번째 줄: arr[0][0] = s[0], arr[0][1] = s[1]

  • 두 번째 줄: arr[1][0] = s[2], arr[1][1] = s[3]

  • 세 번째 줄: arr[2][0] = s[4], arr[2][1] = s[5]

이렇게 2의 배수씩 증가하게 됩니다. 이것을 몫과 나머지로 생각해보면 (x, y)로 표현할 때 arr[x][y] = s[c*x + y]로 표현할 수 있겠네요.

for x in range(r):
    for y in range(c):
        arr[x][y] = s[c*x + y]

그래서 위와 같이 표현할 수 있겠습니다.

그럼 다시 원래 배열인 r = 2, c = 3으로 돌아가면 이것도 마찬가지로 공식으로 배열 s를 인덱싱 할 수 있습니다.

for y in range(c):
    for x in range(r):
        arr[x][y] = s[r*y+x]

위와 같은 코드가 나오는데요. 마찬가지로 s = 'abcdef' 일 때를 예시로 확인해보겠습니다.

  • y = 0, x = 0이면 r*y + x = 0이므로 arr[0][0] = s[0] = 'a'

  • y = 0, x = 1이면 r*y + x = 1이므로 arr[1][0] = s[1] = 'b'

  • y = 1, x = 0이면 r*y + x = 2이므로 arr[2][0] = s[2] = 'c'

  • y = 1, x = 1이면 r*y + x = 3이므로 arr[2][1] = s[3] = 'd'

  • y = 2, x = 0이면 r*y + x = 4이므로 arr[3][0] = s[4] = 'e'

  • y = 2, x = 1이면 r*y + x = 5이므로 arr[3][1] = s[5] = 'f'

이것을 그림으로 그려보면 아래 사진처럼 2차원 리스트에 값이 넣어지게 됩니다.

마찬가지로 원래 코드에도 똑같은 방식을 적용하면 됩니다.

이해가 잘 가지 않는 분은 문제 링크에 나온 예제 입력 1을 가지고 한 번 해보시길 바랍니다.

s = input()
n = len(s)

divisor = []
for i in range(1, n+1):
    if n % i == 0: divisor.append(i)

if len(divisor) % 2 == 0:
    r = divisor[len(divisor)//2 - 1]
    c = n // r
else:
    r = divisor[len(divisor)//2]
    c = n // r

arr = [[' ']*c for _ in range(r)]

for y in range(c):
    for x in range(r):
        arr[x][y] = s[r*y+x]

이제 배열에 값을 넣었으니 드디어 출력할 차례입니다.

출력은 print()함수에 end인자를 넣어서 출력하거나, 출력할 리스트를 한 곳에 다 저장해서 join을 이용하면 됩니다.

s = input()
n = len(s)

divisor = []
for i in range(1, n+1):
    if n % i == 0: divisor.append(i)

if len(divisor) % 2 == 0:
    r = divisor[len(divisor)//2 - 1]
    c = n // r
else:
    r = divisor[len(divisor)//2]
    c = n // r

arr = [[' ']*c for _ in range(r)]

for y in range(c):
    for x in range(r):
        arr[x][y] = s[r*y+x]

#방법 1
for x in range(r):
    for y in range(c):
        print(arr[x][y], end = '')

#방법 2
ans = []
for x in range(r):
    for y in range(c):
        ans.append(arr[x][y])
print(''.join(ans))

참고로 이 문제는 그냥 s = input()으로 입력 받아 r, c를 구한 다음, 2차원 리스트에 값을 넣지 않고 s의 인덱싱으로 바로 출력을 할 수 있습니다.

이 방법도 마찬가지로 위 코드 17~19줄에 나온 방식과 비슷하게 풀 수 있습니다.

BOJ 12759

틱택토 게임을 하는데 입력값을 받으면서 누가 이겼는지 혹은 비겼는지 출력을 하는 문제입니다.

일단 처음에는 위 사진처럼 게임판을 전부 빈칸으로 만들어야 합니다.

# 방법1
arr = [[' '] * 3 for _ in range(3)]

# 방법2
arr = [[' ', ' ', ' '] for _ in range(3)]

# 방법3
arr = []
for i in range(3):
    arr.append([' '] * 3)

# 방법4
arr = []
for i in range(3):
    arr.append([' ', ' ', ' '])

빈칸을 만드는 2차원 리스트는 이렇게 다양하게 있으니 아무거나 하나 택해서 만들면 됩니다. 저는 방법 1로 하겠습니다.

이제 입력값을 받는 코드를 짜봅시다. 먼저 시작하는 사람을 입력하므로 이것은 player를 줄여서 p라는 변수에 담아두겠습니다.

arr = [[' '] * 3 for _ in range(3)]

p = int(input())

이제 좌표를 받아야 하는데, 문제를 읽어보니까 몇 줄이 입력되는지 모르겠지만 마지막 입력값은 무조건 게임의 승자가 결정된다고 합니다.

그러면 while문을 이용해서 입력값을 받다가 게임이 끝나면 break문을 이용하여 나가면 되겠네요. 게임이 끝나는 유무는 게임의 승자가 누구인지 저장하는 winner 변수를 만들어서 1이 이기면 winner = 1, 2가 이기면 winner = 2, 비기면 winner = 3으로 정하겠습니다.

그리고 매 턴마다 player 턴이 바뀌므로 if / else를 통해 p의 값을 바꾸어줍시다.

arr = [[' '] * 3 for _ in range(3)]

p = int(input())
while 1:
    x, y = map(int,input().split())
    winner = 0
    
    blah blah
    
    if winner > 0: break
    
    if p == 1: p = 2
    else: p = 1

문제를 읽어보니 가장 왼쪽 위의 좌표는 (1, 1), 가장 오른쪽 아래의 좌표는 (3, 3)라고 합니다. 2차원 리스트는 (0, 0)으로 시작하므로 x와 y를 1씩 빼줘야겠습니다.

arr = [[' '] * 3 for _ in range(3)]

p = int(input())
while 1:
    x, y = map(int,input().split())
    x-=1
    y-=1
    winner = 0
    
    blah blah
    
    if winner > 0: break
    
    if p == 1: p = 2
    else: p = 1

이제 플레이어 순서에 따라 O, X를 그려주면 됩니다. 이것은 if문으로 p가 1일 때와 2일 때로 나눠서 각각 O, X를 그려주면 됩니다.

arr = [[' '] * 3 for _ in range(3)]

p = int(input())
while 1:
    x, y = map(int,input().split())
    x-=1
    y-=1
    winner = 0
    
    if p == 1: arr[x][y] = 'O'
    else: arr[x][y] = 'X'
    
    blah blah
    
    if winner > 0: break
    
    if p == 1: p = 2
    else: p = 1

이제 우리는 언제까지 입력을 받는지 모르니까 턴이 끝날 때마다 이긴 사람이 존재하는지 확인해봅시다.

틱택토는 가로줄 3개, 세로줄 3개, 대각선 2개를 확인해야 합니다.

먼저 가로줄은 arr[x][0], arr[x][1], arr[x][2]를 확인해주면 됩니다. 그리고 세로줄은 arr[0][y], arr[1][y], arr[2][y]를 확인해주면 됩니다.

그래서 가로줄과 세로줄은 for문을 이용해서 만들어줄 수 있습니다.

arr = [[' '] * 3 for _ in range(3)]

p = int(input())
while 1:
    x, y = map(int,input().split())
    x-=1
    y-=1
    winner = 0
    
    if p == 1: arr[x][y] = 'O'
    else: arr[x][y] = 'X'
    
    # ===========================================
    
    res = []
    
    for i in range(3):
        res.append([arr[i][0], arr[i][1], arr[i][2]])
    
    for i in range(3):
        res.append([arr[0][i], arr[1][i], arr[2][i]])
    
    # ============================================
    
    blah blah
    
    if winner > 0: break
    
    if p == 1: p = 2
    else: p = 1

대각선은 바로 인덱싱을 이용하여 추가하겠습니다.

arr = [[' '] * 3 for _ in range(3)]

p = int(input())
while 1:
    x, y = map(int,input().split())
    x-=1
    y-=1
    winner = 0
    
    if p == 1: arr[x][y] = 'O'
    else: arr[x][y] = 'X'
    
    # =======================================
    
    res = []
    
    for i in range(3):
        res.append([arr[i][0], arr[i][1], arr[i][2]])
    
    for i in range(3):
        res.append([arr[0][i], arr[1][i], arr[2][i]])
    
    res.append([arr[0][0], arr[1][1], arr[2][2]])
    res.append([arr[0][2], arr[1][1], arr[2][0]])
    
    # ============================================
    
    blah blah
    
    if winner > 0: break
    
    if p == 1: p = 2
    else: p = 1

이렇게 res에 리스트를 넣었으면 ['X', 'X', 'X'] 혹은 ['O', 'O', 'O']와 같은 값이 있는지 확인하면 됩니다. 이것은 멤버 연산자를 이용하여 확인할 수 있습니다.

arr = [[' '] * 3 for _ in range(3)]

p = int(input())
while 1:
    x, y = map(int,input().split())
    x-=1
    y-=1
    winner = 0
    
    if p == 1: arr[x][y] = 'O'
    else: arr[x][y] = 'X'
    
    res = []
    
    for i in range(3):
        res.append([arr[i][0], arr[i][1], arr[i][2]])
    
    for i in range(3):
        res.append([arr[0][i], arr[1][i], arr[2][i]])
    
    res.append([arr[0][0], arr[1][1], arr[2][2]])
    res.append([arr[0][2], arr[1][1], arr[2][0]])
    
    # =======================================
    
    if ['O', 'O', 'O'] in res: winner = 1
    if ['X', 'X', 'X'] in res: winner = 2
    
    # =======================================
    
    blah blah
    
    if winner > 0: break
    
    if p == 1: p = 2
    else: p = 1

그리고 마지막으로 게임이 비긴 상태로 끝났는지 확인해야 합니다.

비기는 것은 일단 winner = 0일 때, for문과 멤버 연산자를 이용하여 res가 가지고 있는 리스트중에 공백(' ')이 있는지 확인하면 되겠네요. 이때 공백이 없으면 winner = 3으로 수정하면 됩니다.

arr = [[' '] * 3 for _ in range(3)]

p = int(input())
while 1:
    x, y = map(int,input().split())
    x-=1
    y-=1
    winner = 0
    
    if p == 1: arr[x][y] = 'O'
    else: arr[x][y] = 'X'
    
    res = []
    
    for i in range(3):
        res.append([arr[i][0], arr[i][1], arr[i][2]])
    
    for i in range(3):
        res.append([arr[0][i], arr[1][i], arr[2][i]])
    
    res.append([arr[0][0], arr[1][1], arr[2][2]])
    res.append([arr[0][2], arr[1][1], arr[2][0]])
    
    if ['O', 'O', 'O'] in res: winner = 1
    if ['X', 'X', 'X'] in res: winner = 2
    
    # =======================================
    
    if winner == 0:
        flag = False # 공백이 있으면 True, 없으면 False
        for i in range(len(res)):
            if ' ' in res[i]: flag = True
        
        if not flag: winner = 3
    
    # =======================================
    
    if winner > 0: break
    
    if p == 1: p = 2
    else: p = 1

이렇게 해서 이제 break문을 나오면 이기는 사람을 출력하거나 비기면 0을 출력하면 되겠네요.

arr = [[' '] * 3 for _ in range(3)]

p = int(input())
while 1:
    x, y = map(int,input().split())
    x-=1
    y-=1
    winner = 0
    
    # 보드판에 O, X 그리기
    if p == 1: arr[x][y] = 'O'
    else: arr[x][y] = 'X'
    
    # 승부가 결정났는지 확인하기 
    res = []
    
    for i in range(3):
        res.append([arr[i][0], arr[i][1], arr[i][2]])
    
    for i in range(3):
        res.append([arr[0][i], arr[1][i], arr[2][i]])
    
    res.append([arr[0][0], arr[1][1], arr[2][2]])
    res.append([arr[0][2], arr[1][1], arr[2][0]])
    
    if ['O', 'O', 'O'] in res: winner = 1
    if ['X', 'X', 'X'] in res: winner = 2
    
    if winner == 0:
        flag = False # 공백이 있으면 True, 없으면 False
        for i in range(len(res)):
            if ' ' in res[i]: flag = True
        if not flag: winner = 3
    
    # 승부가 결정되었을 때
    if winner > 0: break
    
    # 턴 넘기기
    if p == 1: p = 2
    else: p = 1

if winner < 3: print(winner)
else: print(0)

이렇게 해서 틱택토 문제를 풀었습니다. 코드가 굉장히 긴 편인데요. 이렇게 우리가 좀 복잡한 구현 문제를 풀 때는 어떤 기능이 필요한지, 그 기능을 어디에 배치해야 하는지 생각을 하거나 메모장에 기록하면서 문제를 풀어야 하는 능력이 필요합니다.

풀어볼 문제

Last updated