문제
풀이
- BFS를 이용해 Sr, Sc에서 시작해서 상하좌우로 한 칸씩 이동 가능한지 확인하며 모든 좌표를 탐색한다.
- 작은 직사각형 범위 안에 벽(1)이 있거나 직사각형이 격자의 범위를 벗어나면 해당 경우는 이동할 수 없음
- 이미 방문한 좌표이면 이동할 수 없음
- 시작 좌표가 Fr, Fc에 도달하면 지금까지 움직인 거리를 출력한다.
직사각형 범위 안에 벽이 있는지 확인할 때, 직사각형의 모든 좌표를 탐색하면 시간초과가 발생한다.
//시간초과
import java.awt.*;
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 boj_16973 {
static int N, M, H, W, Sr, Sc, Fr, Fc, ans=Integer.MAX_VALUE;
static int[][] board;
static int[] dx, dy;
static boolean[][] visited;
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());
board = new int[N][M];
for(int i=0; i<N; i++){
st = new StringTokenizer(br.readLine());
for(int j=0; j<M; j++){
board[i][j] = Integer.parseInt(st.nextToken());
}
}
st = new StringTokenizer(br.readLine());
H = Integer.parseInt(st.nextToken());
W = Integer.parseInt(st.nextToken());
Sr = Integer.parseInt(st.nextToken())-1;
Sc = Integer.parseInt(st.nextToken())-1;
Fr = Integer.parseInt(st.nextToken())-1;
Fc = Integer.parseInt(st.nextToken())-1;
dx = new int[H]; //행
dy = new int[W]; //열
for(int i=0; i<H; i++)
dx[i] = i;
for(int i=0; i<W; i++)
dy[i] = i;
visited = new boolean[N][M];
bfs(Sr, Sc, 0);
if(ans == Integer.MAX_VALUE){
System.out.println(-1);
}else System.out.println(ans);
}
static void bfs(int x, int y, int cnt){
Queue<Point> q = new LinkedList<>();
q.add(new Point(x, y, cnt));
visited[x][y] = true;
while(!q.isEmpty()){
Point now = q.poll();
if(now.x==Fr && now.y==Fc){
if(ans>now.cnt) ans=now.cnt;
return;
}
boolean isValid = true;
for(int i=0; i<H; i++){
for(int j=0; j<W; j++){
int nx = now.x + dx[i];
int ny = now.y + dy[j];
if(nx<0 || nx>=N || ny<0 || ny>=M || board[nx][ny]==1){
//범위를 벗어나거나 벽을 만남
isValid = false;
break;
}
}
if(!isValid) break;
}
if(isValid){
//직사각형 범위 모두 만족하면 한 칸 이동
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
for(int i=0; i<4; i++){
int nx = now.x + dx[i];
int ny = now.y + dy[i];
if(nx<0 || nx>=N || ny<0 || ny>=M
|| board[nx][ny]==1 || visited[nx][ny])
continue;
q.add(new Point(nx, ny, now.cnt+1));
visited[nx][ny] = true;
}
}
}
}
}
class Point{
int x, y, cnt;
Point(int x, int y, int cnt){
this.x = x;
this.y = y;
this.cnt = cnt;
}
}
따라서 벽의 좌표를 리스트에 저장해두고 벽이 직사각형 안에 포함되는지 확인해야 한다.
코드
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class boj_16973 {
static int N, M, H, W, Sr, Sc, Fr, Fc, ans=Integer.MAX_VALUE;
static int[][] board;
static boolean[][] visited;
static ArrayList<Point> walls;
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());
walls = new ArrayList<>();
board = new int[N][M];
for(int i=0; i<N; i++){
st = new StringTokenizer(br.readLine());
for(int j=0; j<M; j++){
board[i][j] = Integer.parseInt(st.nextToken());
if(board[i][j]==1)
walls.add(new Point(i, j));
}
}
st = new StringTokenizer(br.readLine());
H = Integer.parseInt(st.nextToken());
W = Integer.parseInt(st.nextToken());
Sr = Integer.parseInt(st.nextToken())-1;
Sc = Integer.parseInt(st.nextToken())-1;
Fr = Integer.parseInt(st.nextToken())-1;
Fc = Integer.parseInt(st.nextToken())-1;
visited = new boolean[N][M];
bfs(Sr, Sc, 0);
if(ans == Integer.MAX_VALUE){
System.out.println(-1);
}else System.out.println(ans);
}
static void bfs(int x, int y, int cnt){
Queue<Point> q = new LinkedList<>();
q.add(new Point(x, y, cnt));
visited[x][y] = true;
while(!q.isEmpty()){
Point now = q.poll();
if(now.x==Fr && now.y==Fc){
if(ans>now.cnt) ans=now.cnt;
return;
}
//이동 가능한 경우, 한 칸 이동
for(int i=0; i<4; i++){
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
int nx = now.x + dx[i];
int ny = now.y + dy[i];
//좌표가 격자에서 벗어남
if(nx<0 || nx>=N || ny<0 || ny>=M || visited[nx][ny])
continue;
boolean isValid = true;
//직사각형 크기가 격자에서 벗어남
if(nx+H>N || ny+W>M)
continue;
//직사각형 내부에 벽이 있음
for(int j=0; j<walls.size(); j++){
int wallX = walls.get(j).x;
int wallY = walls.get(j).y;
if(wallX<nx+H && wallY<ny+W && wallX>=nx && wallY>=ny){
isValid = false;
break;
}
}
if(!isValid) continue;
q.add(new Point(nx, ny, now.cnt+1));
visited[nx][ny] = true;
}
}
}
}
class Point{
int x, y, cnt;
Point(int x, int y, int cnt){
this.x = x;
this.y = y;
this.cnt = cnt;
}
Point(int x, int y){
this.x = x;
this.y = y;
}
}
'BOJ' 카테고리의 다른 글
[백준 18248번 / java] 제야의 종 (0) | 2024.11.09 |
---|---|
[백준 14496번 / java] 그대, 그머가 되어 (0) | 2024.11.08 |
[백준 14226번 / java] 이모티콘 (0) | 2024.09.04 |
[백준 14594번 / java] 동방 프로젝트 (small) (0) | 2024.09.04 |
[백준 1342번 / java] 행운의 문자열 (0) | 2024.08.23 |