코딩 테스트

[2022 삼성 코딩테스트 상반기 오후 1번] 코드트리 - 꼬리잡기놀이

코드트리애호가 2023. 9. 29. 22:33

https://www.codetree.ai/training-field/frequent-problems/problems/tail-catch-play?&utm_source=clipboard&utm_medium=text 

 

코드트리 | 코딩테스트 준비를 위한 알고리즘 정석

국가대표가 만든 코딩 공부의 가이드북 코딩 왕초보부터 꿈의 직장 코테 합격까지, 국가대표가 엄선한 커리큘럼으로 준비해보세요.

www.codetree.ai

 

DFS와 Simulation 유형의 기출문제이다. 

 

처음에 DFS가 나와있어서 어라.. 나 BFS만 잔뜩 해봤는데 풀 수 있나 했더니

역시 DFS가 되면 bfs로도 상관없이 되더라. 

 

 

 

 

이번 문제를 풀며 힘들었던 점? 이랄까 

를 정리해보면 

 

 

1.각 팀을 어떻게 저장할 것이고, 관리할 것인지? + move 할때 방향 

 

2. 라운드 마다 던져지는 공의 점화식 

 

 

이다. 그외 나머지는 deque()를 사용해서 쉽게 구현 할 수 있엇다. 

 

-----------------------------------------------

 

의외로 방향? 에 대해서는 큰 문제를 하지 않았다. 

 

 

왜냐? 

 

문제에서 우선순위에 따라 좌우상하 같은 조건도 주어지지 않았고, 

공을 맞으면 방향이 반대로 바뀐다.. 그러면 일일히 사람들의 방향도 +2 를하거나 90도로 꺾어지는 부분에서 또 고려를 해야한다. 하지만 그런거 없이 오직 방향만 바뀌는거면.. 

 

내가 만약 배열에서 인덱스 0부터 끝까지를 머리~몸통~꼬리 라고 정한다면, 

이걸 반대로 reverse() 해서 뒤집어 버리면, 꼬리~몸통~머리로 되는게 아닌가? 싶었다. 

그래서 이걸 유의하고 

 

move() 함수와 같이 이녀석들이 움직일때 4가 움직일 수 있는 방향인데 

점화식을 활용해서 deque을 관리했다. 그다음 움직이는곳이 0,0 이라고 해보자.  

0,1   0,2   1,2   2,2 

라고 한다면,  먼저 (0,1) 를 temp_x,temp_y 에 저장하고

0,2   1,2   2,2   2,2  

이런식으로 <- 한칸씩 옮겨 준다.

그다음 맨 앞에 deque 의 appendleft()를 사용해서 temp값과 새로 움직여야하는 머리값을 차례대로 넣어준다.  

0,1   0,2   1,2   2,2   2,2  

-> temp_x,temp_y appendleft 하고 

0,0   0,1   0,2   1,2   2,2   2,2  

-> 새로 움직이는 좌표를 appendleft 해주고 

 

마지막에 더미 데이터인 꼬리부분을  두번 pop 해서 지워주면, 

0,0   0,1   0,2   1,2

와같이 팀들을 움직이게 할 수 있다. 

 

팀들은 각 [deque(),deque()...] 이런식으로 구현했다.

team = [deque() for _ in range(m) ]

 먼저 전처리과정에서 2차원배열을 쭉 순회하며 maps[i][j] == 1 일때 팀을 하나 잡고 거기서부터 2 를 탐색 (bfs 사용) 하고 마지막에 3이나오면 탐색을 종료하며 그 탐색값을 deque()에 저장 했다. 

 

여기서 문제였던게 만약에 

 

2  1  3

2  0  2

2  2  2 

 

이런식으로 되어있다면... 시작하자마자 3을 발견해서 팀이 0,1과 0,2 두개밖에 나오지 않게 된다. 

그래서 이때 예외 처리를 했다. 

 

처음 팀을 탐색할때, 첫 탐색에서 3이 나온다면, 그냥 무시하게 처리했다. 이거는 탐색할때마다 cnt 를 증가 시켜서 

cnt가 1일때 3을 발견한다면 continue 해버리고, 나머지는 3을 만나면 거기서 종료시키게 처리했다. 

 


생각외로 점화식에서 애먹었는데.. 

 

처음에 문제 조건에서 1부터 n 까지로 글이 나와있길래 

글을 보면서 따라하기 쉽도록  

라운드 수를 for tc in range(1,k+1) : 이렇게 처리했는데 ..

 

그덕에 그런지... 나중에 

x % 4n -> 4n까지 끝나면 그다음 29는 다시 1번과 똑같아야한다. 

이처리에서 뭔가 꼬이기 시작했다. 

x % (4n+1) 을 해버리면 29가 0으로 나오는데, 여기서 +1을 해줘야한다. 

근데 만약에 57이 온다고 치면, 28이 나오는데 28에서 +1을 더해버린다 (?) 그래서 else 문으로 빠져서 이상하게 처리되기 시작했다. 

이걸 하나하나 예외식이나 점화식을 다시 세우기엔 너무 오래걸릴꺼같아서.. 

 

그냥

0~n-1

n~ 2*n-1

2*n ~ 3*n-1

3*n ~ 4n-1 

이렇게 우리가 흔히 아는 0~n-1 처리로 바꿔버리고, 

 

4n이 넘었을때 그냥 x%(4*n) 처리 해버리니까 귀찮게 +1 할필요도 없이 알아서 처리가 됐다. 

 

이부분을 해결하고 실행하니 완료 할 수 있었다. 

 


 

이번에는 한 4시부터 9시까지 조금 여유롭게? 풀었지만 하루내로 풀어서 기분이 좋다. 

역시나 알고리즘 -> 문제에서 요구하는 조건을 어떤방식으로 구현할 것인가? 

를 고려해서 풀었다.

 

모쪼록 6개월전에 비해 못풀던 문제를 풀게 되어서 기분이 좋다.

 

앞으로 코테까지 약 2주 남짓인데..

 

합격해서 코드트리에서 공부해서 합격했다고 떠벌떠벌 자랑하고싶다. 

 

그도그럴게 백준이나 프로그래머스는 질문도와주는 선생님들이 없잖슴~~~~~~

 

 

from collections import deque
import sys
#sys.stdin = open("input.txt",'r')
n,m,k = map(int,input().split())
maps = [list(map(int,input().split())) for _ in range(n)]

deb = 0

dxs=[0,0,-1,1]
dys=[1,-1,0,0]
#각팀당 가지는 배열
team = [deque() for _ in range(m) ]

def in_range(x,y):
    return 0<=x<n and 0<=y<n

#머리를 찾아야함
#초기전처리
head=0
for i in range(n) :
    for j in range(n) :
        if maps[i][j] == 1 :
            #여기서 부터 dx,dy 진행하면서 머리와 꼬리를 찾아 나선다.
            queue = deque()
            visited = [ [False]*n for _ in range(n)]
            visited[i][j] = True
            queue.append((i,j))
            team[head].append((i,j))
            possible = False
            cnt = 0
            while queue :
                cnt += 1
                if possible :
                    break
                x,y = queue.popleft()
                #print(x,y)
                for num in range(4) :
                    nx,ny = x+dxs[num],y+dys[num]
                    if in_range(nx,ny) and maps[nx][ny] == 2 and visited[nx][ny] == False:
                        queue.append((nx,ny))
                        visited[nx][ny] = True
                        team[head].append((nx,ny))
                    elif in_range(nx,ny) and maps[nx][ny] == 3 and cnt == 1 :
                        continue
                    elif in_range(nx,ny) and maps[nx][ny] == 3   :
                        team[head].append((nx,ny))
                        visited[nx][ny] = True
                        possible = True


            if deb:
                for test_c in visited:
                    print(*test_c)
                for test_x in team[head]:
                    print(*test_x)
            head +=1
            if deb:
                print(head)


def update_team() :
    global maps
   
    for i in range(n) :
        for j in range(n) :
            if maps[i][j] > 0 and maps[i][j] < 4 :
                maps[i][j] = 4 #전부다 빈공간으로 초기화
    for level in range(len(team)) :
        queues = team[level]
       
        for que in range(len(queues)) :
            x,y = queues[que]
            if que == 0 :
                maps[x][y] = 1
            elif que == (len(queues)-1) :
                maps[x][y] = 3
            else :  
                maps[x][y] = 2
   
    return

def move() :
   
    for level in range(len(team)) :
        queue = team[level]
        temp_queue = queue.copy()
        #앞만 보고 갈 수있게 처리해야한다.
        x,y = queue.popleft() #머리부터 가져와

        for i in range(4):
            nx,ny = x+dxs[i],y+dys[i]
            if in_range(nx,ny) and (maps[nx][ny] == 4 or maps[nx][ny] == 3 ) :
                next_x,next_y = nx,ny
                break
        # 방향을 이제 찾았다.
        # 덱 값을 한칸씩 옮겨주고 마지막 빼고 머리 붙이기
        temp_x,temp_y = temp_queue[0]
        for item in range(0,len(temp_queue)-1) :
            temp_queue[item] = temp_queue[item+1]
        temp_queue.pop()
        temp_queue.pop()
        temp_queue.appendleft((temp_x,temp_y))
        temp_queue.appendleft((next_x,next_y))  
        if deb:
            print(temp_queue)
        team[level] = temp_queue.copy()
   
    update_team()
    if deb:
        print(maps)
    return

def if_hit(x,y) :
    if in_range(x,y):
        if 1<= maps[x][y] <= 3 :
            return True
    else :
        return False
    return False

def change(x,y) :
    return_point = 0
    for level in range(len(team)) :
        queue = team[level]
        if (x,y) in queue :
            idx = queue.index((x,y))
            return_point = (idx +1)**2 #부딫힌사람 몇번째
        #이제 맞았으니 해당 큐 위치를 바꿔줘야한다.
            queue.reverse()
            update_team()
            if deb:
                print(queue)
        #바꾸고 난다음에 바로 리턴해줘서 끝내야한다. 안그러면 계속 바꿈.
            return return_point
   
   
    return #에러나게 만듬

def throw(round) :
    point = 0
   
    #여기서 라운드는 0~n-1 까지 들어오는걸 유의.

    if round >= 4*n :  
        round = round%(4*n) # 4n이 넘어도 계속 반복되게
   
    if 0 <= round <= n-1 :
        for j in range(n) :
            if if_hit(round,j) : # 만약 맞았다면,  
                point += change(round,j)
                return point
           
    elif n <= round <= 2*n-1 :
        round = round%(n)
        for row in range(n-1,-1,-1) :
            if if_hit(row,round) : # 만약 맞았다면,  
                point += change(row,round)
                return point
       
    elif (2*n) <= round <= 3*n-1 :
        round = round%((2*n)) # 인덱스 처리.
        round = (n-round-1)
        for col in range(n-1,-1,-1) :
            if if_hit(round,col) : # 만약 맞았다면,  
                point += change(round,col)
                return point

    else : # 3n+1 <= round <= 4n-1
        round = round%(3*n)
        round = (n-round-1)
        for row in range(n) :
            if if_hit(row,round) : # 만약 맞았다면,  
                point += change(row,round)
                return point

   
    return point

answer = 0
for round in range(k) :
    #각팀이 머리사람 따라서 한칸 이동
    move()
   
    #공이 선을따라 던져진다. 몇포인트를 먹었는지 반환해야함.
    answer += throw(round)
    #print(answer)


print(answer)