6 분 소요

1. 문제 접근 방법 및 아이디어

  1. 초기 상태 파악
    • 2차원 배열을 입력받으며 전차의 초기 위치(r,c)와 방향(d)을 저장합니다.
  2. 명령어 해석
    • 문자열로 주어진 명령을 순차적으로 읽으며 Moveshoot을 분기 처리합니다.
  3. 이동 로직
    • 방향을 먼저 바꾼다.
    • 바라보는 앞 칸이 평지(.)이고 격자 범위 내인지 확인한다.
    • 조건 만족 시 현재 위치를 평지로 만들고 새로운 위치로 이동한다.
  4. 포탄 발사 로직
    • 전차의 현재 위치에서 현재 방향으로 while루프를 돌며 포탄을 전진시킨다.
    • 벽(*,#)을 만나거나 맵 밖으로 나갈 떄까지 반복한다.
    • 벽의 종류에 따라 파괴 여부를 결정한다.
  5. 결과 출력
    • 최종 맵 상태를 출력합니다.


2. 필요 개념 및 알고리즘

  1. 시뮬레이션
    • 문제 자체가 특정 알고리즘을 요구하기보다 이 조건대로 움직여라라는 명령형 구조이기 때문입니다.
  2. 델파 배열
    • 상하좌우 이동을 간결하게 처리하는 테크닉입니다.
    • 문제에서 상(U), 하(D), 좌(L), 우(R) 4가지 방향이 주어지므로, 이를 인덱스(0~3)로 매핑하여 처리할 수 있습니다.


3. 어려워하는 포인트 또는 실수하는 포인트

  1. 어려웠던 포인트
    • 방향 전환과 이동을 동시에 처리해야 하는 점.
    • 이동과 발상에 필요한 아이디어가 떠오르지 못한 점
  2. 실수하는 포인트
    • 이동 실패 시 방향 전화
      • 이동을 못 하더라도 전차의 방향은 반드시 바뀌어야 합니다.
    • 시뮬레이션 구현 도중 오탈자


4. 코드 풀이

4.1 초기 전차 정보 파악

static int pr, pc, pd; // 전차의 좌표, 방향
static char tank[] = { '^', 'v', '<', '>' }; // 전차의 모양

// 맵 정보 입력
for (int i = 0; i < H; i++) {
     String row = br.readLine();
     for (int j = 0; j < W; j++) {
          map[i][j] = row.charAt(j);
          
          // 초기 전차 정보 초기화
          for (int d = 0; d < 4; d++) {
               if (map[i][j] == tank[d]) {
                    pr = i;
                    pc = j;
                    pd = d;
               }
          }
     }
} // 맵 정보 입력 끝
  • 초기 전차의 위치와 방향을 탐색합니다.
  • 상하좌우 방향으로 표현된 전차의 모양 중 하나를 찾습니다.

4.2 명령어 해석

// 명령어 처리 함수
static void process(char cmd) {

	if (cmd == 'S') {
		shoot();
	} else {	
		move(cmd);			
	}

} // 명령어 처리 함수 종료
  • shootmove를 분리해서 처리합니다.
  • shoot가 혼자 독립적이라서 shoot를 먼저 처리합니다.

4.3 방향 설정

// 명령어를 인덱스로 매핑하는 함수
static int getDir(char cmd) {
	
     if(cmd == 'U') return 0;
	if(cmd == 'D') return 1;
	if(cmd == 'L') return 2;
	if(cmd == 'R') return 3;
		
	return -1;

} // 매핑 함수 종료
  • U, D, L, R에 해당하는 인덱스를 반환합니다.

4.4 이동 로직

// 이동 함수
static void move(char cmd) {
     // 현재 방향
	int cd = getDir(cmd);

	// 방향 갱신
	pd = cd;
	map[pr][pc] = tank[cd];

	// 다음 좌표
	int nr = pr + dr[cd];
	int nc = pc + dc[cd];

	// 범위에 벗어나면 종료
	if (!(0 <= nr && nr < H && 0 <= nc && nc < W)) {
		return;
	}

	// 평지를 발견하면 이동
	// 이전에 맵은 평지로 바꾼다.
	if (map[nr][nc] == '.') {
		map[nr][nc] = tank[cd];
		map[pr][pc] = '.';
		pr = nr;
		pc = nc;
	}

} // 이동 함수 종료
  • 현재의 방향을 구하고, 방향을 먼저 바꿉니다.
  • 바라보는 앞 칸이 평지이고, 격자 범위 내인지 확인합니다.
  • 조건 만족 시 현재 위치를 평지로 만들고 새로운 위치로 이동합니다.

4.5 포탄 발사 로직

// 포탄 발사 함수
static void shoot() {

     // 현재 좌표
     int cr = pr;
     int cc = pc;

	// 범위에 벗어나거나 벽에 붙이히면 종료
	while (true) {
	     // 다음 좌표
		int nr = cr + dr[pd];
		int nc = cc + dc[pd];

		// 범위에 벗어나면 종료
		if (!(0 <= nr && nr < H && 0 <= nc && nc < W)) {
			break;
		}

		// 강철로 만들어진 벽을 만나면 종료
		if (map[nr][nc] == '#') {
			break;
		}

		// 벽돌로 만들어진 벽을 만나면 종료
		// 벽돌은 파괴되고, 평지가된다.
		if (map[nr][nc] == '*') {
			map[nr][nc] = '.';
			break;
		}

		// 좌표 갱신
		cr = nr;
		cc = nc;

	}

} // 포탄 발상 함수 종료
  • 전차의 현재 위치에서 현재 방향으로 while루프를 돌며 포탄을 전진시킵니다.
  • 벽을 만나거나 맵 박으로 나갈 떄까지 반복합니다.
  • 벽의 종류(*,#)에 따라 파괴 여부를 결정합니다.

4.6 전체 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Solution {

	static int H, W; // 맵의 높이, 너비
	static int N; // 입력의 개수
	static int pr, pc, pd; // 전차의 좌표, 방향
	static String cmds; // 입력 모음
	static char[][] map; // 맵의 정보
	static int dr[] = { -1, 1, 0, 0 }; // 행 방향 배열
	static int dc[] = { 0, 0, -1, 1 }; // 열 방향 배열
	static char tank[] = { '^', 'v', '<', '>' }; // 전차의 모양

	public static void main(String[] args) throws NumberFormatException, IOException {
		// TODO Auto-generated method stub
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringBuilder sb = new StringBuilder();

		int T = Integer.parseInt(br.readLine().trim());

		// tc수행
		for (int tc = 1; tc <= T; tc++) {

			// H, W 입력
			StringTokenizer st = new StringTokenizer(br.readLine());
			H = Integer.parseInt(st.nextToken());
			W = Integer.parseInt(st.nextToken());

			// 맵 정보 초기화
			map = new char[H][W];

			// 맵 정보 입력
			for (int i = 0; i < H; i++) {
				String row = br.readLine();
				for (int j = 0; j < W; j++) {
					map[i][j] = row.charAt(j);

					// 초기 전차 정보 초기화
					for (int d = 0; d < 4; d++) {
						if (map[i][j] == tank[d]) {
							pr = i;
							pc = j;
							pd = d;
						}
					}

				}
			} // 맵 정보 입력 끝

			// 입력 개수, 정보 입력
			N = Integer.parseInt(br.readLine().trim());
			cmds = br.readLine();

			// 처리
			for (int i = 0; i < N; i++) {
				process(cmds.charAt(i));
			}

			// 출력 내용 추가
			sb.append("#" + tc + " ");
			for (int i = 0; i < H; i++) {
				for (int j = 0; j < W; j++) {
					sb.append(map[i][j]);
				}
				sb.append("\n");
			}

		} // tc 종료

		// 출력
		System.out.println(sb);

	} // 메인 종료

	// 명령어 처리 함수
	static void process(char cmd) {

		if (cmd == 'S') {
			shoot();
		} else {
			
			move(cmd);
			
		}

	} // 명령어 처리 함수 종료

	// 이동 함수
	static void move(char cmd) {
		
		int cd = getDir(cmd);

		// 방향 갱신
		pd = cd;
		map[pr][pc] = tank[cd];

		// 다음 좌표
		int nr = pr + dr[cd];
		int nc = pc + dc[cd];

		// 범위에 벗어나면 종료
		if (!(0 <= nr && nr < H && 0 <= nc && nc < W)) {
			return;
		}

		// 평지를 발견하면 이동
		// 이전에 맵은 평지로 바꾼다.
		if (map[nr][nc] == '.') {
			map[nr][nc] = tank[cd];
			map[pr][pc] = '.';
			pr = nr;
			pc = nc;
		}

	} // 이동 함수 종료

	// 포탄 발사 함수
	static void shoot() {

		// 현재 좌표
		int cr = pr;
		int cc = pc;

		// 범위에 벗어나거나 벽에 붙이히면 종료
		while (true) {
			// 다음 좌표
			int nr = cr + dr[pd];
			int nc = cc + dc[pd];

			// 범위에 벗어나면 종료
			if (!(0 <= nr && nr < H && 0 <= nc && nc < W)) {
				break;
			}

			// 강철로 만들어진 벽을 만나면 종료
			if (map[nr][nc] == '#') {
				break;
			}

			// 벽돌로 만들어진 벽을 만나면 종료
			// 벽돌은 파괴되고, 평지가된다.
			if (map[nr][nc] == '*') {
				map[nr][nc] = '.';
				break;
			}

			// 좌표 갱신
			cr = nr;
			cc = nc;

		}

	} // 포탄 발상 함수 종료
	
     // 명령어를 인덱스로 매핑하는 함수
	static int getDir(char cmd) {
		if(cmd == 'U')
			return 0;
		if(cmd == 'D')
			return 1;
		if(cmd == 'L')
			return 2;
		if(cmd == 'R')
			return 3;
		
		return -1;
	} // 매핑 함수 종료

}

4.7 개선 버전

4.7.1 초기 전차 초기화 개선

for (int i = 0; i < H; i++) {
     String row = br.readLine();
     for (int j = 0; j < W; j++) {
          map[i][j] = row.charAt(j);

          // 전차 위치와 방향 초기화
          int d = "^v<>".indexOf(map[i][j]);
          if (d != -1) {
               pr = i; 
               pc = j; 
               pd = d;
        }
    }
}
  • indexOf를 활용해서 for문으로 탐색하는 것을 대신했습니다.

4.7.2 getDir 함수 최적화

static int getDir(char cmd) {
    // 문자열에서 해당 문자의 위치(index)를 반환하는 기법
    return "UDLR".indexOf(cmd); 
}
  • indexOf를 활용해서 if문을 여러 번 쓰는 것을 대신했습니다.

카테고리:

업데이트: