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

 

16933번: 벽 부수고 이동하기 3

첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 1,000), K(1 ≤ K ≤ 10)이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다. (1, 1)과 (N, M)은 항상 0이라고 가정하자.

www.acmicpc.net

코드설명

BFS 문제입니다.

 

이 문제에서 가장 유의해야할점들입니다.

  1. 시작점에서 현재 낮/밤인지 고려합니다.
    1. 기준점에서 이동할 지점에서는 현재 낮/밤에서 반대로 밤/낮 으로 검사합니다.
  2. 만약 이동하지 못하는경우, 이동하지 않고 같은칸에 머물러있는것이 가능합니다.
    1. 이때, 아무것도 안할경우의 처리하는것을 고려해야합니다. 처음에 저같은경우 

 

아래의 반례를 이해한다면 풀 수 있습니다.

3 3 3
011
111
110

정답:7

https://www.acmicpc.net/board/view/38832

 

 

처음에 틀렸던 코드의 주요로직을 살펴보겠습니다.

아래의 코드를 보면 

map[nr][nc] == '0' (빈칸) 낮/밤 의 경우를 나누었습니다.

map[nr][nc] == '1' (벽) 낮/밤 의 경우를 나누었습니다.

 

여기서 발생하는 문제점은, 벽을 부순 이후에 본인의 위치에 계속 존재하려고할떄 발생합니다.

이유는, map[nr][nc] == '1'(벽)일떄 아침인경우는 다른 곳의 벽을 부술 수 있으니 이동한다고 가정합니다.

하지만, 저녁인경우 다른곳으로 이동이 불가능한데 이떄, 저는 dir = 0 ~ dir = 5 로 처리했으므로 모든 상하좌우현재위치 이렇게 5가지 방향을 모두 순회하므로 다른 방향으로도 벽을 부수지 않고 들어가기에 위의 제시한 예제의 답이 5로 나옵니다.

 

그렇기에 dir ==0 일떄, ( 현재 위치로 다시 움직일떄, 가만히 있을때 ) 벽의 위치에서 다시 존재할경우를 고려하면 됩니다.

조건문으로는 dir == 0 을 추가해주면됩니다.

for(int dir=0;dir<5;dir++) {
    int nr = r + dx[dir];
    int nc = c + dy[dir];
    int nMorningZeroNightOne = morningZeroNightOne == 0 ? 1 : 0;

    if(nr < 0 || nr >= N || nc < 0 || nc >= M) continue;
    if(map[nr][nc] == '0') { //벽이 아닐경우 그냥 이동합니다. 밤에도 이동가능합니다.
        if(morningZeroNightOne == 0) {
            if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
            visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
            q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));	
        }
        else if(morningZeroNightOne == 1) {
            if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
            visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
            q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));
        }
    }

    if(map[nr][nc] == '1') { //아침에만 부술 수 있습니다.
        if(morningZeroNightOne == 0) { //아침일경우 벽을 부술 수 있습니다.
            if(wallBreakCount >= K) continue; //이미 부술 수 있는 벽을 다 부쉈다면,
            if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount+1] == true) continue;
            visited[nr][nc][nMorningZeroNightOne][wallBreakCount + 1] = true;
            q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount + 1));	
        }
        else if(morningZeroNightOne == 1) { //저녁일경우 벽을 부수지 못하며, 다른곳으로 0 으로만 이동가능합니다.
            if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
            visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
            q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));
        }

    }

}

 

dir==0을 개선한코드

for(int dir=0;dir<5;dir++) {
    int nr = r + dx[dir];
    int nc = c + dy[dir];
    int nMorningZeroNightOne = morningZeroNightOne == 0 ? 1 : 0;

    if(nr < 0 || nr >= N || nc < 0 || nc >= M) continue;
    if(map[nr][nc] == '0') { //벽이 아닐경우 그냥 이동합니다. 밤에도 이동가능합니다.
        if(morningZeroNightOne == 0) {
            if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
            visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
            q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));	
        }
        else if(morningZeroNightOne == 1) {
            if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
            visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
            q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));
        }
    }

    if(map[nr][nc] == '1') { //아침에만 부술 수 있습니다.
        if(morningZeroNightOne == 0) { //아침일경우 벽을 부술 수 있습니다.
            if(wallBreakCount >= K) continue; //이미 부술 수 있는 벽을 다 부쉈다면,
            if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount+1] == true) continue;
            visited[nr][nc][nMorningZeroNightOne][wallBreakCount + 1] = true;
            q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount + 1));	
        }

        if( dir == 0) {
            if(morningZeroNightOne == 1 ) { //저녁일경우 벽을 부수지 못하며, 다른곳으로 0 으로만 이동가능합니다.
                if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
                visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
                q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));
            }    					
        }


    }

}

 

 

위와 같이 특정한 곳을 모두 고려하여 진행할 수도 있지만, 단순히 만약 dir == 0 이라면 벽이 존재하든 아니든 전혀 중요하지 않습니다. 그저 해당 위치에 존재하기만 하면 되니 아래와 같이 수정한다면 훨씬 깔끔한 코드가 탄생합니다.

좀 아쉬운점은 애초에 dir 상하좌우로 여전히 냅두고, 아예 바깥에다가 현재 위치를 삽입하는 방안으로 짯으면 헷갈리지 않았을 것 같습니다. 

for(int dir=0;dir<5;dir++) {
    int nr = r + dx[dir];
    int nc = c + dy[dir];
    int nMorningZeroNightOne = morningZeroNightOne == 0 ? 1 : 0;

    if(nr < 0 || nr >= N || nc < 0 || nc >= M) continue;
    if(map[nr][nc] == '0') { //벽이 아닐경우 그냥 이동합니다. 밤에도 이동가능합니다.
        if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
        visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
        q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));
    }

    if(map[nr][nc] == '1' && morningZeroNightOne == 0) { //아침에만 부술 수 있습니다.
        if(wallBreakCount >= K) continue; //이미 부술 수 있는 벽을 다 부쉈다면,
        if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount+1] == true) continue;
        visited[nr][nc][nMorningZeroNightOne][wallBreakCount + 1] = true;
        q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount + 1));
    }

    if(dir == 0) { //만약 아무것도 안움직일경우
        if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
        visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
        q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));
    }

}

 

코드

정답코드

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

public class Main {
	
	public static int N, M, K;
	public static char[][] map;
	public static boolean[][][][] visited;
	public static int[] dx= {0, -1,1,0,0};
	public static int[] dy = {0, 0,0,-1,1};
	public static int answer = 0;
    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());
    	K = Integer.parseInt(st.nextToken());
    	
    	visited = new boolean[N][M][2][K+1];
    	map = new char[N][M];
    	for(int i=0;i<N;i++) {
    		st = new StringTokenizer(br.readLine());
    		map[i] = st.nextToken().toCharArray();
    	}
    	simulate();
    	if(answer == 0) {
    		System.out.println("-1");
    	}else {
    		System.out.println(answer);	
    	}
    	
    }
    
    public static void simulate() {
    	Queue<Node> q = new LinkedList<>();
    	q.offer(new Node(0, 0, 1, 0, 0));
    	visited[0][0][0][0] = true;
    	while(!q.isEmpty()) {
    		Node temp = q.poll();
    		int r = temp.r;
    		int c = temp.c;
    		int moveCnt = temp.moveCnt;
    		int morningZeroNightOne = temp.morningZeroNightOne;
    		int wallBreakCount = temp.wallBreakCount;
			if(r == N-1 && c == M-1) {
				answer = moveCnt; 
				return ;
			}    		
    		for(int dir=0;dir<5;dir++) {
    			int nr = r + dx[dir];
    			int nc = c + dy[dir];
    			int nMorningZeroNightOne = morningZeroNightOne == 0 ? 1 : 0;
    			
    			if(nr < 0 || nr >= N || nc < 0 || nc >= M) continue;
    			if(map[nr][nc] == '0') { //벽이 아닐경우 그냥 이동합니다. 밤에도 이동가능합니다.
    				if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
    				visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
    				q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));
    			}
    			
    			if(map[nr][nc] == '1' && morningZeroNightOne == 0) { //아침에만 부술 수 있습니다.
    				if(wallBreakCount >= K) continue; //이미 부술 수 있는 벽을 다 부쉈다면,
    				if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount+1] == true) continue;
    				visited[nr][nc][nMorningZeroNightOne][wallBreakCount + 1] = true;
    				q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount + 1));
    			}
    			
    			if(dir == 0) { //만약 아무것도 안움직일경우
    				if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
    				visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
    				q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));
    			}
    			
    		}
    		
    		
    	}
    	
    }
    
}

class Node{
	int r;
	int c;
	int moveCnt;
	int morningZeroNightOne = 0;
	int wallBreakCount = 0;
	public Node(int r, int c, int moveCnt, int morningZeroNightOne, int wallBreakCount) {
		this.r=r;
		this.c=c;
		this.moveCnt = moveCnt;
		this.morningZeroNightOne = morningZeroNightOne;
		this.wallBreakCount = wallBreakCount;
		
	}
}

 

 

틀린코드

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

public class Main {
	
	public static int N, M, K;
	public static char[][] map;
	public static boolean[][][][] visited;
	public static int[] dx= {0, -1,1,0,0};
	public static int[] dy = {0, 0,0,-1,1};
	public static int answer = 0;
    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());
    	K = Integer.parseInt(st.nextToken());
    	
    	visited = new boolean[N][M][2][K+1];
    	map = new char[N][M];
    	for(int i=0;i<N;i++) {
    		st = new StringTokenizer(br.readLine());
    		map[i] = st.nextToken().toCharArray();
    	}
    	simulate();
    	if(answer == 0) {
    		System.out.println("-1");
    	}else {
    		System.out.println(answer);	
    	}
    	
    }
    
    public static void simulate() {
    	Queue<Node> q = new LinkedList<>();
    	q.offer(new Node(0, 0, 1, 0, 0));
    	visited[0][0][0][0] = true;
    	while(!q.isEmpty()) {
    		Node temp = q.poll();
    		int r = temp.r;
    		int c = temp.c;
    		int moveCnt = temp.moveCnt;
    		int morningZeroNightOne = temp.morningZeroNightOne;
    		int wallBreakCount = temp.wallBreakCount;
			if(r == N-1 && c == M-1) {
				answer = moveCnt; 
				return ;
			}    		
    		for(int dir=0;dir<5;dir++) {
    			int nr = r + dx[dir];
    			int nc = c + dy[dir];
    			int nMorningZeroNightOne = morningZeroNightOne == 0 ? 1 : 0;
    			
    			if(nr < 0 || nr >= N || nc < 0 || nc >= M) continue;
    			if(map[nr][nc] == '0') { //벽이 아닐경우 그냥 이동합니다. 밤에도 이동가능합니다.
    				if(morningZeroNightOne == 0) {
        				if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
        				visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
        				q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));	
    				}
    				else if(morningZeroNightOne == 1) {
        				if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
        				visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
        				q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));
    				}
    			}
    			
    			if(map[nr][nc] == '1') { //아침에만 부술 수 있습니다.
    				if(morningZeroNightOne == 0) { //아침일경우 벽을 부술 수 있습니다.
        				if(wallBreakCount >= K) continue; //이미 부술 수 있는 벽을 다 부쉈다면,
        				if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount+1] == true) continue;
        				visited[nr][nc][nMorningZeroNightOne][wallBreakCount + 1] = true;
        				q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount + 1));	
    				}
    				else if(morningZeroNightOne == 1) { //저녁일경우 벽을 부수지 못하며, 다른곳으로 0 으로만 이동가능합니다.
        				if(visited[nr][nc][nMorningZeroNightOne][wallBreakCount] == true) continue;
        				visited[nr][nc][nMorningZeroNightOne][wallBreakCount] = true;
        				q.offer(new Node(nr, nc, moveCnt + 1, nMorningZeroNightOne, wallBreakCount));
    				}
    				
    			}
    			
    			
    		}
    		
    		
    	}
    	
    }
    
}

class Node{
	int r;
	int c;
	int moveCnt;
	int morningZeroNightOne = 0;
	int wallBreakCount = 0;
	public Node(int r, int c, int moveCnt, int morningZeroNightOne, int wallBreakCount) {
		this.r=r;
		this.c=c;
		this.moveCnt = moveCnt;
		this.morningZeroNightOne = morningZeroNightOne;
		this.wallBreakCount = wallBreakCount;
		
	}
}

+ Recent posts