강승현입니다
    • 홈
    • 태그
    • 방명록

    카테고리

    • 전체 글 (119) N
      • 후기 (38)
        • 경험 (15)
        • SSAFY (9)
        • 코딩테스트 (3)
        • 넥스터즈 (6)
        • 회고 (5)
      • Degrees (2)
      • Tech (1) N
        • Java&Spring (13)
        • IDE (1)
        • Node.js (2)
        • Git (3)
        • Server (3)
        • DevOps (0)
        • OS (3)
        • Javascript (1)
        • C,C++ (1)
        • Python (2)
        • 알고리즘 (1)
        • 트러블슈팅 (1)
      • OnlineJudge (45)
      • 정보전달 (2)
    OnlineJudge

    백준 3190 뱀 풀이 python,java

    CODe_byCODe_·2021. 1. 12. 23:15

    문제로이동

     

    3190번: 뱀

     'Dummy' 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다. 게임

    www.acmicpc.net


    목차

       


      python
      java

      런타임 에러(IndexError)

      반례를 찾기 전까진 "맞왜틀"을 호소했습니다. IndexError가 일어나는 이유를 몰랐거든요.. 하지만 검증 과정에서 문제가 있었습니다.

      아래의 소스코드를 보면 필드를 벗어나는 경우를 검증하지 않습니다. BFS/DFS때 흔히 하는 0<=nx<n 이런 검증 말이죠.

      그래서 런타임 에러가 발생했고 이를 조건문 검증 후 제출했지만 틀렸습니다가 떴네요. 아래에서 추가 설명하겠습니다.

      else:#그냥 길인 경우
                  field[ny][nx]=2#그 자리에 머리를 넣어줌
                  field[ty][tx]=0#꼬리 있던 자리를 없애줌
                  for tdx,tdy in [[0,1],[1,0],[-1,0],[0,-1]]:#다음 몸통 자리를 찾음
                      ndx,ndy = tx+tdx,ty+tdy
                      if field[ndy][ndx]==2:#몸통을 찾았다면
                          tailq.append([ndx,ndy])#그 몸통이 꼬리가 된다.
                          break            
              q.append([nx,ny])

      틀렸습니다

      지나다니는 길을 0 뱀의 머리를 빨간색 2, 꼬리를 파란색 2라고 가정하겠습니다.

      아래 그림을 보시면 이해가 바로 되실까요?

       

                         
                         
                         
              2 2 2      
              2 2 2      
                2 2      
                2 2      
                         
                         
                         

       

      런타임 에러의 소스코드를 보시게 되면 꼬리는 이어져 있는 몸통을 찾기 위해 상, 하, 좌, 우로 서칭을 하게 됩니다.

      자 위의 경우 몸통과 머리가 모두 붙어 있을 때 이어진 몸통을 찾는 게 아니라 엉뚱한 곳을 찾게 되면서 꼬리의 위치가 꼬여버리는 문제가 생깁니다. 그래서 이 부분을 자각하고 리스트와 덱(Deque)을 대공사 했습니다.


      문제 설명

      '뱀 게임', '지렁이 게임'이라고도 합니다. 한 칸씩 이동하며 사과를 먹는 게임이죠.

      사과를 먹으면 몸의 길이가 1씩 증가합니다. 단, 사과를 다 먹는다고 해서 게임이 끝나는 게 아니라 벽에 닿거나 자신의 몸통과 겹쳐 버리면 게임이 끝나는 구조입니다.


      소스 코드

      Python3

      필드에 뱀(2), 사과(1), 길(0)을 따로 저장해 두고 뱀의 좌표(SNAKE)를 저장했습니다. 뱀의 좌표(SNAKE)는 머리와 꼬리만 움직이면 되는 구조이므로 끝 부분의 append, pop에 용이한 Deque을 사용했습니다.

      drcControl 함수는 뱀의 방향 전환을 해주고 옳은 방향으로 direction을 수정해 주는 역할입니다.

      i의 값이 튀어 indexerror를 만들기 전에 조건문으로 주어진 범위 내에서만 작동할 수 있도록 지정해 두었습니다.

       

      풀고 나니 평소 풀던 DFS/BFS보다 정말 쉬웠던 문제인데, 게임 형태로 문제가 나오니 많이 당황했던 것 같습니다.

       

      ##########################################################
      import sys
      from collections import deque
      # direction = [[0,1],[-1,0],[1,0],[0,-1]] #for BFS
      input = sys.stdin.readline
      def ip():return input().rstrip()
      def lip():return list(ip())
      def ips():return ip().split()
      def ii():return int(input())
      def mii():return map(int,ips())
      def lmii():return list(mii())
      ##########################################################
      n = ii()#보드의 크기
      k = ii()#사과의 개수
      field = [[0]*n for _ in range(n)]
      for _ in range(k):
          a,b = mii()
          field[a-1][b-1]=1   #1은 사과
      l = ii()#뱀의 방향 변환 횟수
      turn = deque()
      for _ in range(l):
          a,b = ips()
          turn.append([int(a),b])
      turn = deque(sorted(turn,key=lambda x:x[0]))#시간순 정렬
      def drcControl(direc,turnd):#현재 방향과 바꾸는 방향
          s = [[1,0],[0,1],[-1,0],[0,-1]]
          for i,[a,b] in enumerate(s):
              if [a,b]==direc:
                  if turnd=='L':
                      if i==0:i=4
                      return s[i-1]
                  else:
                      if i==3:i=-1
                      return s[i+1]
      def solve():
          global k
          direction = [1,0]#오른쪽이 기본 방향
          field[0][0]=2
          SNAKE = deque([[0,0]])#뱀 꼬리-몸통-머리 구조를 나타낸다.
          move = 0
          while SNAKE:
              x,y = SNAKE[-1]#머리        
              if turn and turn[0][0]==move:#방향 전환 먼저
                  direction = drcControl(direction,turn[0][1])
                  turn.popleft()
              dx,dy = direction
              nx,ny = dx+x,dy+y
      
              if 0>nx or nx>=n or 0>ny or ny>=n or field[ny][nx]>1:#엔딩 조건
                  return move+1
              
              if field[ny][nx]==1:#사과를 만난 경우
                  field[ny][nx]=2#그 자리에 머리를 넣어줌
                  SNAKE.append([nx,ny])#우측에 좌표를 추가해서 머리를 갱신해줌
              else:#그냥 길인 경우
                  field[ny][nx]=2#그 자리에 머리를 넣어줌    
                  tx,ty=SNAKE.popleft()#꼬리를 빼준다.
                  field[ty][tx]=0#꼬리 있던 자리를 없애줌    
                  SNAKE.append([nx,ny])#머리를 넣어준다.                
              move+=1#시간 증가    
      print(solve())

      Java8 (21.02.02 추가)

      81~84줄 주석을 해제하면 뱀이 이동하는 모습을 볼 수 있습니다.

      package boj3190;
      
      import java.io.BufferedReader;
      import java.io.IOException;
      import java.io.InputStreamReader;
      import java.util.ArrayDeque;
      import java.util.Deque;
      import java.util.StringTokenizer;
      
      public class Main {
      	static class Pos{
      		int x,y;
      		Pos(int x,int y){
      			this.x = x;
      			this.y = y;
      		}
      	}
      	static class Move{
      		int move;
      		char direction;
      		public Move(int move, char direction) {
      			this.move = move;
      			this.direction = direction;
      		}
      	}
      	static BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
      	static StringTokenizer st;
      	static int N,K,L;
      	static int dirX[] = {1,0,-1,0};//우하좌상
      	static int dirY[] = {0,1,0,-1};
      	static StringBuilder sb = new StringBuilder();
      	public static void main(String[] args) throws IOException{
      		st = new StringTokenizer(bf.readLine());
      		N = Integer.parseInt(st.nextToken());//필드수
      		int[][] field = new int[N][N];
      		
      		st = new StringTokenizer(bf.readLine());
      		K = Integer.parseInt(st.nextToken());//사과
      		
      		for(int i =0;i<K;i++) {
      			st = new StringTokenizer(bf.readLine());
      			field[Integer.parseInt(st.nextToken())-1][Integer.parseInt(st.nextToken())-1]=1;//사과는 1
      		}		
      		
      		Deque<Pos> snake = new ArrayDeque<Main.Pos>();//뱀 관리 꼬리-몸통-머리 순
      		snake.add(new Pos(0,0));//몸추가
      		field[0][0]=2;//뱀은 2
      		Deque<Move> query = new ArrayDeque<>();//정수X, 문자C
      		
      		st = new StringTokenizer(bf.readLine());
      		L = Integer.parseInt(st.nextToken());//방향전환 횟수
      		
      		for(int i =0;i<L;i++) {//쿼리삽입
      			st = new StringTokenizer(bf.readLine());
      			query.add(new Move(Integer.parseInt(st.nextToken()),st.nextToken().charAt(0)));
      		}
      
      		int cur_direction = 0;//오른쪽으로 스타트
      		int answer =0 ;
      		while(true) {
      			Move tmp = query.poll();
      			int move = 99999;//쿼리를 다 수행했더라도 뱀은 계속 움직여야함.
      			if(tmp != null)
      				move = tmp.move;
      			for(int i =answer;i<move;i++) {
      				//다음 이동되는 좌표
      				int nx = snake.peekLast().x+dirX[cur_direction];
      				int ny = snake.peekLast().y+dirY[cur_direction];
      				if(0<=nx && nx<N && 0<=ny && ny<N && field[ny][nx]!=2) {
      					if(field[ny][nx]==1) {//사과가있는 경우 몸을 늘린다.
      						field[ny][nx]=2;
      						snake.addLast(new Pos(nx,ny));//머리를 사과자리에.
      					}
      					else {//단순이동
      						Pos tmp2 = snake.pollFirst();//꼬리제거
      						field[tmp2.y][tmp2.x]=0;
      						snake.addLast(new Pos(nx,ny));//머리로이동
      						field[ny][nx]=2;
      					}
      					answer++;
      //					System.out.println("=================="+answer);
      //					for(int z =0;z<field.length;z++) {
      //						System.out.println(Arrays.toString(field[z]));
      //					}
      				}
      				else {//벽에 또는 몸통에 부딪히면 게임종료
      					System.out.println(answer+1);
      					return;
      				}
      			}
      			//움직인 이후 방향전환
      			if(tmp.direction=='D') {
      				cur_direction++;
      				if(cur_direction>3)cur_direction=0;
      			}
      			else if(tmp.direction=='L') {
      				cur_direction--;
      				if(cur_direction<0)cur_direction=3;
      			}
      		}
      	}
      }
      반응형
      저작자표시 비영리 변경금지 (새창열림)
      'OnlineJudge' 카테고리의 다른 글
      • 백준 17135 캐슬디펜스 풀이 python, java
      • 백준 15954 인형들(카카오 2018 예선 문제)
      • 백준 9328 열쇠 BFS
      • 백준 2638 치즈 풀이 python,java
      CODe_
      CODe_
      개발과 관련된 다양한 정보를 몰입감있게 전달합니다.
      최신 글

      티스토리툴바