알고리즘/백준

[백준] 1103 게임 - DP + 깊이우선탐색(DFS, 재귀) JAVA

passionfruit200 2023. 11. 16. 12:16

https://www.acmicpc.net/problem/1103

 

1103번: 게임

줄에 보드의 세로 크기 N과 가로 크기 M이 주어진다. 이 값은 모두 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에 보드의 상태가 주어진다. 쓰여 있는 숫자는 1부터 9까지의 자연수 또는

www.acmicpc.net

코드설명

DP와 깊이우선탐색(DFS, 재귀)를 활용합니다.

 

DFS를 활용한 이유는 문제에서 맨 마지맊까지 움직이고 계산하도록 지문에 나와있기 때문입니다.

BFS를 활용해도 될 것 같아 보이지만, DFS를 활용했습니다.

 

simulate 함수 설명입니다.

  • DFS(깊이 우선 탐색)를 활용하여 모든 가능한 경로를 탐색하고, 각 경로의 이동 횟수를 함수가 실행될떄마다 +1씩 증가시켜 함수의 이동횟수를 확인할 수 있습니다.
  • 재귀 호출을 통해 모든 가능한 경로를 탐색하고, 각 위치에서의 최대 이동 횟수를 dp 배열에 저장합니다.
  • 만약 이미 해당 위치를 방문한 적이 있다면 사이클이 발생한 것으로 판단하여 2500을 반환합니다.

문제에서 놓쳤었던점은 DP만을 활용하여 최대 이동횟수를 계산하려했지만, 만약 똑같은 위치를 돌아서 다시 해당 위치에 도달한다면 DP로는 알아낼 수 없습니다. 그렇기에 Visited 배열을 활용하여 만약 해당 위치에 다시 들어온다면 바로 사이클로 판별하여 최대값 2500 을 return 해줍니다.

코드

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

public class Main {
	
	public static int N, M, K;
	public static int answer;
	public static int[][] map;
	public static int[] dx = {-1,1,0,0};
	public static int[] dy = {0,0,-1,1};
	public static boolean[][] visited;
	public static int[][] dp;
	public static void main(String[] args) throws IOException{
    	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    	StringTokenizer st = new StringTokenizer(br.readLine());
    	
    	N = Integer.parseInt(st.nextToken());
    	M = Integer.parseInt(st.nextToken());
    	map = new int[N][M];
    	visited = new boolean[N][M];
    	dp = new int[N][M];
    	
    	for(int i=0;i<N;i++) {
    		st = new StringTokenizer(br.readLine());
    		String str = st.nextToken();
    		for(int j=0;j<M;j++) {
    			map[i][j] = str.charAt(j) - '0';	
    		}
    		
		}
    	
    	visited[0][0] = true;
    	int answer = simulate(new Node(0, 0, 0, 0));
    	if(answer >= 2500) {
    		System.out.println("-1");
    	}else {
    		System.out.println(answer + 1);	
    	}
			
    	
	}
	
	//사이클 판별하는 방법으로 visited를 사용해야하는데. visited[][] , 즉 최대 움직일 수 있는 회수보다 
	public static int simulate(Node start) {
		if(dp[start.r][start.c] != 0) return dp[start.r][start.c]; //dp가 갱신되었다면 이미 해당 공간은 다 탐색되었다는 의미입니다. 하지만 이것으로 싸이클 판별을 할 수 없는 이유는 모두 탐색이 끝나고 재귀로 나오기에 그렇습니다.
		int result = 0;
		Node temp = start;
		for(int dir = 0; dir < 4; dir++) {
			int nr = temp.r + dx[dir] * map[temp.r][temp.c];
			int nc = temp.c + dy[dir] * map[temp.r][temp.c];
			
			if(nr < 0 || nr >= N || nc < 0 || nc >= M) {
				continue;
			}
			if(map[nr][nc] == 'H' - '0') {
				continue;
			}
			if(visited[nr][nc] == true) {
				return 2500;
			}
			visited[nr][nc] = true;
			result = Math.max(result, simulate(new Node(nr, nc, dir, temp.cnt + 1)) + 1);
			visited[nr][nc] = false;
		}
		
		return dp[start.r][start.c] = result;
	}

	
}
class Node{
	int r;
	int c;
	int dir;
	int cnt;
	public Node(int r, int c, int dir, int cnt) {
		this.r=r;
		this.c = c;
		this.dir = dir;
		this.cnt = cnt;
	}
}