
문제 설명
가장 최근에 나왔던 삼성 SW 역량 기출 문제로 알고 있습니다.
약 1시간 15분 가량 소요되었던 문제이며 굉장히 재밌게 풀었습니다만 시간을 좀 더 단축해야겠다는 생각이 강하게 들었습니다.
이번 문제는 각 기능별(함수)로 설명 하겠습니다.
그리고 작동되는 순서는 아래 나열된 순서와 동일합니다.
위치별 인덱싱
이 문제를 해결하면서 상어를 중심으로 소용돌이 모양으로 구슬을 이동하고, 변화하고, 폭발하고의 과정이 거쳐지는 것을 알 수 있습니다.
그래서 각 좌표별로 번호를 매겨주었습니다.

이 인덱싱으로 아래의 모든 함수(구슬의 이동, 변화, 폭파)를 쉽게 구현할 수 있었습니다.
N의 크기가 작아서 python의 dictionary(Java는 hashmap)를 사용했고 인덱싱이 끝난 변수는 아래와 같이 값이 들어가게 됩니다.

중심좌표인 상어의 위치(1번)부터 소용돌이를 그려가며 인덱싱을 최초 1회만 해준다면 그 이후는 인덱스로 접근하면 편하겠죠?
indexing = {}
def init():#각 필드에 번호와 좌표를 매칭시킨다
    x = (N+1)//2-1#필드 중심
    y = (N+1)//2-1
    index = 1   #현재 필드의 번호
    turn = 1    #현재 방향
    depth = 1 #나아가는 길이(점점 증가)
    #서 - 남 // 동 - 북 (3,2,4,1)    
    #index % 4
    # 4(0) = 북
    # 1 = 서
    # 2 = 남
    # 3 = 동
    tmpdir = [[0,-1],[-1,0],[0,1],[1,0]]
    tmpfield = [[0]*N for _ in range(N)]
    cnt=0#같은 방향으로 이동한 횟수
    while x>-1 and y>-1:        
        indexing[index]=[x,y]#번호에 좌표를 넣어줌        
        tmpfield[y][x]=index
        x+=tmpdir[turn][0]
        y+=tmpdir[turn][1]        
        cnt+=1
        index+=1        
        if cnt==depth:#깊이와 동일하게 움직였다면?
            if turn in [0,2]:
                depth+=1#남, 북일때는 depth 증가
            turn=(turn+1)%4#방향 전환
            cnt=0
상어로부터 시작하는 소용돌이의 규칙을 보면 서-남-동-북의 방향을 반복하되, 길이가 점점 길어지는 것을 알 수 있습니다.
이 길이가 길어지는 기준은 남쪽 이후, 북쪽 이후로 매 번 동일하기 때문에 남, 북 방향일 때 depth를 증가시켜주어 다음번 길이에 반영이 됩니다.
상어의 블리자드
상하좌우 방향과 거리가 주어집니다. 정말 단순하게도 해당 방향으로 거리만큼의 구슬을 제거하면 됩니다.
def blizzard(d,s):#상어의 블리자드
    cx = (N+1)//2-1
    cy = (N+1)//2-1
    for i in range(1,s+1):
        nx = cx+direction[d][0]*i
        ny = cy+direction[d][1]*i
        if field[ny][nx]:
            field[ny][nx]=0    
구슬의 이동
투 포인터(start,end)를 사용했고 단순하게 빈 공간에 구슬을 하나씩 당겨오는 방법으로 구현했습니다.
def moveMarble():#구슬의 이동
    start = 2#시작 인덱스 2
    end = 3#다음 인덱스 3로 시작
    while start < N*N and end<=N*N:
        while start<N*N:#비어있는 필드 찾기
            sx,sy = indexing[start]
            if not field[sy][sx]:#비어있다면
                break
            start+=1
        else:return#더 이상 빈 칸이 없다
        if end<=start:
            end = start+1
        while end<=N*N:
            ex,ey = indexing[end]
            if field[ey][ex]:#구슬이 있다면?
                break
            end+=1
        else:return#더이상 당길 구슬이 없다
        field[sy][sx]=field[ey][ex]
        field[ey][ex]=0      
구슬의 폭발
연속된 구슬이 4개 이상일 때 폭발시켜야 합니다. 단, 이 때 폭발되는 구슬들은 정답 출력을 위해 반드시 기록하여야 합니다.
저는 broken_marble이라는 배열을 사용했으며 1번구슬, 2번구슬, 3번구슬의 갯수를 매 번 저장합니다.
def bombMarble():#구슬의 파괴
    result = False
    start = 2
    end = 3
    same = 1 #같은 구슬 수 카운팅
    while start < N*N and end<=N*N:
        sx,sy = indexing[start]
        ex,ey = indexing[end]
        if field[sy][sx] == field[ey][ex]:#구슬 같으면?
            same+=1
            end+=1
        else:#구슬이 달라졌을때
            if same>=4:#동일 구슬 4개 이상은 폭파 대상
                result = True   #한 번이라도 폭파되면 결과값 True
                #start ~ end-1까지 폭파한다
                for i in range(start,end):
                    bx,by = indexing[i]
                    broken_marble[field[by][bx]-1]+=1#폭파되는 구슬 카운팅
                    field[by][bx]=0#폭파
            same=1
            start=end
            end=start+1
    return result#False라는건 더이상 폭파할 구슬이 없다는 뜻
여기서 return으로 True/False를 반환하게 되는데, 폭발이 일어났다는 뜻은 구슬을 당겨야(moveMarble)하기 때문입니다.
반대로, 폭발이 일어나지 않았다면 더 이상 구슬을 이동할 필요가 없다는 뜻이기도 합니다.
구슬의 변화
def changeMarble():#구슬의 변화
    newfield = [[0]*N for _ in range(N)]
    index = 2
    start = 2
    end = 3
    same = 1    
    #그룹에 대한 구슬 정의 -> A : 구슬의 갯수, B : 구슬의 번호
    while start < N*N and end<=N*N:
        sx,sy = indexing[start]
        ex,ey = indexing[end]
        if field[sy][sx] == field[ey][ex]:#구슬 같으면?
            same+=1
            end+=1
        else:#구슬이 달라졌을때
            ix,iy = indexing[index]
            newfield[iy][ix] = same  #A:구슬갯수
            ix,iy = indexing[index+1]
            newfield[iy][ix] = field[sy][sx]#B:구슬번호
            index+=2            
            same=1
            start=end
            end=start+1
        if index>N*N:
            break
    for i in range(N):
        for j in range(N):
            field[i][j] = newfield[i][j]
구슬의 변화는 현재 field에 즉각 반영하기 보다는 새로운 newfield에 반영하여 복사하는 방식을 선택했습니다.
소스 코드
Python
import sys
input = sys.stdin.readline
N,M = map(int,input().split())
field = [list(map(int,input().strip().split())) for _ in range(N)]
skills = [list(map(int,input().strip().split())) for _ in range(M)]#상어 스킬
direction = [[0,0],[0,-1],[0,1],[-1,0],[1,0]]#상하좌우
broken_marble = [0,0,0]#파괴한 구슬의 수
indexing = {}
def init():#각 필드에 번호와 좌표를 매칭시킨다
    x = (N+1)//2-1#필드 중심
    y = (N+1)//2-1
    index = 1
    turn = 1    #현재 방향
    depth = 1
    #서 - 남 // 동 - 북 (3,2,4,1)    
    #index % 4
    # 4(0) = 북
    # 1 = 서
    # 2 = 남
    # 3 = 동
    tmpdir = [[0,-1],[-1,0],[0,1],[1,0]]
    tmpfield = [[0]*N for _ in range(N)]
    cnt=0#같은 방향으로 이동한 횟수
    while x>-1 and y>-1:        
        indexing[index]=[x,y]#번호에 좌표를 넣어줌        
        tmpfield[y][x]=index
        x+=tmpdir[turn][0]
        y+=tmpdir[turn][1]        
        cnt+=1
        index+=1        
        if cnt==depth:#깊이와 동일하게 움직였다면?
            if turn in [0,2]:
                depth+=1#남, 북일때는 depth 증가
            turn=(turn+1)%4#방향 전환
            cnt=0
    
def changeMarble():#구슬의 변화
    newfield = [[0]*N for _ in range(N)]
    index = 2
    start = 2
    end = 3
    same = 1    
    #그룹에 대한 구슬 정의 -> A : 구슬의 갯수, B : 구슬의 번호
    while start < N*N and end<=N*N:
        sx,sy = indexing[start]
        ex,ey = indexing[end]
        if field[sy][sx] == field[ey][ex]:#구슬 같으면?
            same+=1
            end+=1
        else:#구슬이 달라졌을때
            ix,iy = indexing[index]
            newfield[iy][ix] = same  #A:구슬갯수
            ix,iy = indexing[index+1]
            newfield[iy][ix] = field[sy][sx]#B:구슬번호
            index+=2            
            same=1
            start=end
            end=start+1
        if index>N*N:
            break
    for i in range(N):
        for j in range(N):
            field[i][j] = newfield[i][j]
#연속하는 구슬이 4개 이상일 때 파괴된다.
def bombMarble():#구슬의 파괴
    result = False
    start = 2
    end = 3
    same = 1 #같은 구슬 수 카운팅
    while start < N*N and end<=N*N:
        sx,sy = indexing[start]
        ex,ey = indexing[end]
        if field[sy][sx] == field[ey][ex]:#구슬 같으면?
            same+=1
            end+=1
        else:#구슬이 달라졌을때
            if same>=4:#동일 구슬 4개 이상은 폭파 대상
                result = True   #한 번이라도 폭파되면 결과값 True
                #start ~ end-1까지 폭파한다
                for i in range(start,end):
                    bx,by = indexing[i]
                    broken_marble[field[by][bx]-1]+=1#폭파되는 구슬 카운팅
                    field[by][bx]=0#폭파
            same=1
            start=end
            end=start+1
    return result#False라는건 더이상 폭파할 구슬이 없다는 뜻
def moveMarble():#구슬의 이동
    start = 2#시작 인덱스 2
    end = 3#다음 인덱스 3로 시작
    while start < N*N and end<=N*N:
        while start<N*N:#비어있는 필드 찾기
            sx,sy = indexing[start]
            if not field[sy][sx]:#비어있다면
                break
            start+=1
        else:return#더 이상 빈 칸이 없다
        if end<=start:
            end = start+1
        while end<=N*N:
            ex,ey = indexing[end]
            if field[ey][ex]:#구슬이 있다면?
                break
            end+=1
        else:return#더이상 당길 구슬이 없다
        field[sy][sx]=field[ey][ex]
        field[ey][ex]=0            
def blizzard(d,s):#상어의 블리자드
    cx = (N+1)//2-1
    cy = (N+1)//2-1
    for i in range(1,s+1):
        nx = cx+direction[d][0]*i
        ny = cy+direction[d][1]*i
        if field[ny][nx]:
            # broken_marble[field[ny][nx]-1]+=1 #블리자드로 파괴된 구슬도 합산되는가?
            field[ny][nx]=0    
def testPrint():
    for i in range(N):
        for j in range(N):
            print(field[i][j],end=' ')
        print()
init()
for skill in skills:
    d,s = skill
    blizzard(d,s)
    moveMarble()
    while bombMarble():
        moveMarble()#이동과 폭파를 반복한다
    changeMarble()
print(broken_marble[0]+broken_marble[1]*2+broken_marble[2]*3)Java8
#
![[Codility] Lesson4 FrogRiverOne 풀이 Python](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2FbDP5nh%2Fbtq7SMs0Dm3%2FAAAAAAAAAAAAAAAAAAAAAJp3HocLbh4RaXwI5HLN6f8kPmiOhreNw8DtklCRhsSy%2Fimg.png%3Fcredential%3DyqXZFxpELC7KVnFOS48ylbz2pIh7yKj8%26expires%3D1761922799%26allow_ip%3D%26allow_referer%3D%26signature%3DRnAHvgJ7%252FfYy7cSuCrxAJedhH2c%253D) 
                              ![[Codility] Lesson4 PermCheck 풀이 Python](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdna%2FbNNMdb%2Fbtq7C3Qu3nr%2FAAAAAAAAAAAAAAAAAAAAAOHBYiP3-69_tqS6LMp7cfCuNUOWsBVFbqZSWaqQH0B5%2Fimg.png%3Fcredential%3DyqXZFxpELC7KVnFOS48ylbz2pIh7yKj8%26expires%3D1761922799%26allow_ip%3D%26allow_referer%3D%26signature%3DQZ5F2m90LtbVyR3DhyQnI334D2Q%253D) 
                               
                              