우리는 그동안 리스트 안에 문자열이나 숫자를 넣어서 문제를 푸는 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이라는 리스트로 묶으려고 합니다. 그러면 위에서 언급한 내용대로 대괄호 [ ] 를 사용해주면 됩니다.
arr = []for i inrange(8): arr.append(list(input()))ans =0
이제 arr[i][j] == 'F'인지 확인하는 코드를 2중 for문으로 만들어봅시다.
arr = []for i inrange(8): arr.append(list(input()))ans =0for i inrange(8):for j inrange(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 inrange(8): arr.append(list(input()))ans =0for i inrange(8): color = i %2for j inrange(8):if color ==0and 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 inrange(8): arr.append(list(input()))ans =0for i inrange(8): color = i %2for j inrange(8):if color ==0and arr[i][j] =='F': ans +=1 color = (color +1) %2
이제 ans를 출력하면 되겠네요.
arr = []for i inrange(8): arr.append(list(input()))ans =0for i inrange(8): color = i %2for j inrange(8):if color ==0and arr[i][j] =='F': ans +=1 color = (color +1) %2print(ans)
참고로 흰색 칸과 검은색 칸은 어떤 규칙을 가지고 있습니다.
이렇게 좌표를 넣어보면 (x, y)일 때 x+y가 짝수면 하얀 칸, x+y가 홀수면 검은 칸인 것을 알 수 있습니다.
그래서 color의 값을 color = (i + j) % 2로 설정할 수 있습니다.
arr = []for i inrange(8): arr.append(list(input()))ans =0for i inrange(8):for j inrange(8): color = (i + j) %2if color ==0and arr[i][j] =='F': ans +=1print(ans)
BOJ 10864
각 학생의 친구가 몇 명인지 출력하라는 문제입니다.
이 문제는 딕셔너리나 1차원 리스트로 풀 수 있지만 2차원 리스트로 문제를 풀어보겠습니다.
먼저 입력값을 받아봅시다.
n, m =map(int,input().split())for i inrange(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 _ inrange(n)]for i inrange(m): a, b =map(int,input().split())
그런데 이 friend라는 리스트는 만약 n = 5라도 인덱싱은 friend[4]까지만 있으므로 a와 b를 입력 받으면 1씩 빼주거나, friend리스트의 길이를 n+1로 설정합시다.
# 방법 1n, m =map(int,input().split())friend = [[] for _ inrange(n)]for i inrange(m): a, b =map(int,input().split()) a -=1 b -=1# 방법 2n, m =map(int,input().split())friend = [[] for _ inrange(n+1)]for i inrange(m): a, b =map(int,input().split())
저는 주로 방법 1로 문제를 풀고 있으므로 방법 1로 진행하겠습니다.
이제 friend[a]에 b를 추가하고, friend[b]에 a를 추가해주면 됩니다.
n, m =map(int,input().split())friend = [[] for _ inrange(n)]for i inrange(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 _ inrange(n)]for i inrange(m): a, b =map(int,input().split()) a -=1 b -=1 friend[a].append(b) friend[b].append(a)for i inrange(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 inrange(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 inrange(1, n+1):if n % i ==0: divisor.append(i)iflen(divisor)%2==0: r = divisor[len(divisor)//2-1] c = n // relse: 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 inrange(1, n+1):if n % i ==0: divisor.append(i)iflen(divisor)%2==0: r = divisor[len(divisor)//2-1] c = n // relse: r = divisor[len(divisor)//2] c = n // r# 방법 1arr = [[' ']*c for _ inrange(r)]# 방법 2arr = []for i inrange(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 inrange(c):# (x,y)에서 x값을 다 움직여야 y값이 1 증가하므로 for x inrange(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 inrange(r):for y inrange(c): arr[x][y] = s[c*x + y]
그래서 위와 같이 표현할 수 있겠습니다.
그럼 다시 원래 배열인 r = 2, c = 3으로 돌아가면 이것도 마찬가지로 공식으로 배열 s를 인덱싱 할 수 있습니다.
for y inrange(c):for x inrange(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 inrange(1, n+1):if n % i ==0: divisor.append(i)iflen(divisor)%2==0: r = divisor[len(divisor)//2-1] c = n // relse: r = divisor[len(divisor)//2] c = n // rarr = [[' ']*c for _ inrange(r)]for y inrange(c):for x inrange(r): arr[x][y] = s[r*y+x]
이제 배열에 값을 넣었으니 드디어 출력할 차례입니다.
출력은 print()함수에 end인자를 넣어서 출력하거나, 출력할 리스트를 한 곳에 다 저장해서 join을 이용하면 됩니다.
s =input()n =len(s)divisor = []for i inrange(1, n+1):if n % i ==0: divisor.append(i)iflen(divisor)%2==0: r = divisor[len(divisor)//2-1] c = n // relse: r = divisor[len(divisor)//2] c = n // rarr = [[' ']*c for _ inrange(r)]for y inrange(c):for x inrange(r): arr[x][y] = s[r*y+x]#방법 1for x inrange(r):for y inrange(c):print(arr[x][y], end ='')#방법 2ans = []for x inrange(r):for y inrange(c): ans.append(arr[x][y])print(''.join(ans))
참고로 이 문제는 그냥 s = input()으로 입력 받아 r, c를 구한 다음, 2차원 리스트에 값을 넣지 않고 s의 인덱싱으로 바로 출력을 할 수 있습니다.
이 방법도 마찬가지로 위 코드 17~19줄에 나온 방식과 비슷하게 풀 수 있습니다.
BOJ 12759
틱택토 게임을 하는데 입력값을 받으면서 누가 이겼는지 혹은 비겼는지 출력을 하는 문제입니다.
빈칸을 만드는 2차원 리스트는 이렇게 다양하게 있으니 아무거나 하나 택해서 만들면 됩니다. 저는 방법 1로 하겠습니다.
이제 입력값을 받는 코드를 짜봅시다. 먼저 시작하는 사람을 입력하므로 이것은 player를 줄여서 p라는 변수에 담아두겠습니다.
arr = [[' '] *3for _ inrange(3)]p =int(input())
이제 좌표를 받아야 하는데, 문제를 읽어보니까 몇 줄이 입력되는지 모르겠지만 마지막 입력값은 무조건 게임의 승자가 결정된다고 합니다.
그러면 while문을 이용해서 입력값을 받다가 게임이 끝나면 break문을 이용하여 나가면 되겠네요. 게임이 끝나는 유무는 게임의 승자가 누구인지 저장하는 winner 변수를 만들어서 1이 이기면 winner = 1, 2가 이기면 winner = 2, 비기면 winner = 3으로 정하겠습니다.
그리고 매 턴마다 player 턴이 바뀌므로 if / else를 통해 p의 값을 바꾸어줍시다.
arr = [[' '] *3for _ inrange(3)]p =int(input())while1: x, y =map(int,input().split()) winner =0 blah blahif winner >0:breakif p ==1: p =2else: p =1
문제를 읽어보니 가장 왼쪽 위의 좌표는 (1, 1), 가장 오른쪽 아래의 좌표는 (3, 3)라고 합니다. 2차원 리스트는 (0, 0)으로 시작하므로 x와 y를 1씩 빼줘야겠습니다.
arr = [[' '] *3for _ inrange(3)]p =int(input())while1: x, y =map(int,input().split()) x-=1 y-=1 winner =0 blah blahif winner >0:breakif p ==1: p =2else: p =1
이제 플레이어 순서에 따라 O, X를 그려주면 됩니다. 이것은 if문으로 p가 1일 때와 2일 때로 나눠서 각각 O, X를 그려주면 됩니다.
arr = [[' '] *3for _ inrange(3)]p =int(input())while1: x, y =map(int,input().split()) x-=1 y-=1 winner =0if p ==1: arr[x][y] ='O'else: arr[x][y] ='X' blah blahif winner >0:breakif p ==1: p =2else: 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 = [[' '] *3for _ inrange(3)]p =int(input())while1: x, y =map(int,input().split()) x-=1 y-=1 winner =0if p ==1: arr[x][y] ='O'else: arr[x][y] ='X'# =========================================== res = []for i inrange(3): res.append([arr[i][0], arr[i][1], arr[i][2]])for i inrange(3): res.append([arr[0][i], arr[1][i], arr[2][i]])# ============================================ blah blahif winner >0:breakif p ==1: p =2else: p =1
대각선은 바로 인덱싱을 이용하여 추가하겠습니다.
arr = [[' '] *3for _ inrange(3)]p =int(input())while1: x, y =map(int,input().split()) x-=1 y-=1 winner =0if p ==1: arr[x][y] ='O'else: arr[x][y] ='X'# ======================================= res = []for i inrange(3): res.append([arr[i][0], arr[i][1], arr[i][2]])for i inrange(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 blahif winner >0:breakif p ==1: p =2else: p =1
이렇게 res에 리스트를 넣었으면 ['X', 'X', 'X'] 혹은 ['O', 'O', 'O']와 같은 값이 있는지 확인하면 됩니다. 이것은 멤버 연산자를 이용하여 확인할 수 있습니다.
arr = [[' '] *3for _ inrange(3)]p =int(input())while1: x, y =map(int,input().split()) x-=1 y-=1 winner =0if p ==1: arr[x][y] ='O'else: arr[x][y] ='X' res = []for i inrange(3): res.append([arr[i][0], arr[i][1], arr[i][2]])for i inrange(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 =1if ['X','X','X'] in res: winner =2# ======================================= blah blahif winner >0:breakif p ==1: p =2else: p =1
그리고 마지막으로 게임이 비긴 상태로 끝났는지 확인해야 합니다.
비기는 것은 일단 winner = 0일 때, for문과 멤버 연산자를 이용하여 res가 가지고 있는 리스트중에 공백(' ')이 있는지 확인하면 되겠네요. 이때 공백이 없으면 winner = 3으로 수정하면 됩니다.
arr = [[' '] *3for _ inrange(3)]p =int(input())while1: x, y =map(int,input().split()) x-=1 y-=1 winner =0if p ==1: arr[x][y] ='O'else: arr[x][y] ='X' res = []for i inrange(3): res.append([arr[i][0], arr[i][1], arr[i][2]])for i inrange(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 =1if ['X','X','X'] in res: winner =2# =======================================if winner ==0: flag =False# 공백이 있으면 True, 없으면 Falsefor i inrange(len(res)):if' 'in res[i]: flag =Trueifnot flag: winner =3# =======================================if winner >0:breakif p ==1: p =2else: p =1
이렇게 해서 이제 break문을 나오면 이기는 사람을 출력하거나 비기면 0을 출력하면 되겠네요.
arr = [[' '] *3for _ inrange(3)]p =int(input())while1: 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 inrange(3): res.append([arr[i][0], arr[i][1], arr[i][2]])for i inrange(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 =1if ['X','X','X'] in res: winner =2if winner ==0: flag =False# 공백이 있으면 True, 없으면 Falsefor i inrange(len(res)):if' 'in res[i]: flag =Trueifnot flag: winner =3# 승부가 결정되었을 때if winner >0:break# 턴 넘기기if p ==1: p =2else: p =1if winner <3:print(winner)else:print(0)
이렇게 해서 틱택토 문제를 풀었습니다. 코드가 굉장히 긴 편인데요. 이렇게 우리가 좀 복잡한 구현 문제를 풀 때는 어떤 기능이 필요한지, 그 기능을 어디에 배치해야 하는지 생각을 하거나 메모장에 기록하면서 문제를 풀어야 하는 능력이 필요합니다.