BOJ G1 4991 로봇 청소기 JAVA
4991번: 로봇 청소기
각각의 테스트 케이스마다 더러운 칸을 모두 깨끗한 칸으로 바꾸는 이동 횟수의 최솟값을 한 줄에 하나씩 출력한다. 만약, 방문할 수 없는 더러운 칸이 존재하는 경우에는 -1을 출력한다.
www.acmicpc.net
문제 읽기
일단 처음 읽었을 때는 비트마스킹을 쓰는 문제인가? 싶었다. 맵의 크기가 20x20으로 작긴 하지만, 같은 칸을 여러 번 방문할 수 있다는 조건이 있었기 때문에, 큐에 visited를 계속 넣으면 잘못하면 메모리초과가 날 수도 있다고 생각했다. 그런데 비트마스킹을 어떻게 하는지 잘 생각이 안 나서 일단 다른 방법을 또 생각했다.
두 번째로 생각한 건, 더러운 곳을 각각 하나의 노드로 보고, 로봇 청소기(시작 위치)와 더러운 곳 사이의 거리, 그리고 노드간 거리를 다 구해두고, 순열로 더러운 곳을 방문할 순서를 완탐으로 구해서 최소 거리를 계산하는 방식이다.
이 방식으로 문제를 해결했다.
문제 풀기
크게 생각할 문제는 없다. 단순히 다음 과정을 따라서 문제를 해결하면 된다.
- 입력 받을 때 로봇 청소기의 위치(시작 위치)와 더러운 곳의 위치를 저장한다.
- BFS를 이용해 미리 최소 거리를 구한다.
- 로봇 청소기~더러운 곳 의 최소 거리를 startDist[] 배열에 저장한다.
- 더러운 곳~다른 더러운 곳의 최소 거리를 dist[][] 배열에 저장한다.
- 더러운 곳을 permutation하여 줄 세운다
- 줄을 다 세우면 그때마다 미리 구한 거리 값을 이용해 총 최소 거리를 계산하고 갱신한다.
- 최종 최소 거리를 출력한다.
실수한 부분
근데 실수한 부분이 있다. 제출했더니 계속 10퍼센트에서 틀렸다.
질문 게시판 반례도 다 넣어보고, 글도 다 읽어봤는데 나에게 해당되는 내용이 없었다. 그리고 코드도 다시 찬찬히 뜯어보면서 초기화가 안된 게 있는지 확인했는데 그런 것도 없었다.
그래서 질문 게시판에서 엄청 많은 테케가 있는 글이 있었는데, 이걸 넣어보고 깨달았다.
글 읽기 - 테케 공유
댓글을 작성하려면 로그인해야 합니다.
www.acmicpc.net
너무 많아서 그냥 일단 다 넣어봤는데, 저렇게 튀는 값이 있었다.
알고 보니 dist 배열의 초기값이 너무 커서 더하는 과정에서 오버플로우가 생긴 것..
그래서 이 부분을 고쳐줬더니 해결됐다.
<고치는 법>
- INF 크기 줄인다
- 더하는 과정에서 INF면 그냥 그 값은 버린다.
코드
package ps.GraphSearch;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class BOJ_G1_4991_로봇청소기 {
/*
구상
1. 더러운 칸 하나를 하나의 노드로 보고, 모든 더러운 칸들 사이의 거리를 구한다.
1.1 여기서 아무 노드도 갈 수 없는 곳이 있으면(거리가 다 무한대) -1을 출력하고 끝낸다.
2. 순열을 사용해서 더러운 칸들을 줄세운다.
2.1 줄 다 세우면 그 순서대로 이동하는 데에 걸리는 비용을 계산하여, 최소를 저장한다
*/
static class Pos{
int r, c;
int depth;
public Pos(int r, int c){
this.r = r;
this.c = c;
}
public Pos(int r, int c, int depth){
this.r = r;
this.c = c;
this.depth = depth;
}
}
static int H, W;
static char[][] map;
static int[][] intMap;
static Pos start;
static List<Pos> dirty; //노드들 저장
static int[] startDist; //시작점에서 노드들까지의 거리 저장
static int[][] dist; //더러운 노드들 사이의 거리 저장
static int INF = Integer.MAX_VALUE / 21;
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
while(true){
StringTokenizer st = new StringTokenizer(in.readLine());
W = Integer.parseInt(st.nextToken());
H = Integer.parseInt(st.nextToken());
if (H == 0 && W == 0) break;
map = new char[H][W];
intMap = new int[H][W];
dirty = new ArrayList<>();
int idx = 0;
for (int i = 0; i < H; i++){
map[i] = in.readLine().toCharArray();
Arrays.fill(intMap[i], -1);
for (int j = 0; j < W; j++){
if (map[i][j] == 'o') start = new Pos(i, j);
else if (map[i][j] == '*') {
dirty.add(new Pos(i, j));
intMap[i][j] = idx++;
}
}
}
//거리 구하기
//dist 초기화
startDist = new int[dirty.size()];
dist = new int[dirty.size()][dirty.size()];
Arrays.fill(startDist, INF);
getDist(start, startDist); //start부터 다른 곳까지의 거리
for (int i = 0; i < dirty.size(); i++){
Arrays.fill(dist[i], INF);
dist[i][i] = 0;
Pos pos = dirty.get(i);
getDist(pos, dist[i]);
}
// System.out.println(Arrays.toString(startDist));
//순열로 줄세우기
numbers = new int[dirty.size()];
used = new boolean[dirty.size()];
minCost = Integer.MAX_VALUE;
permutation(dirty.size(), 0); //총 숫자, cnt
sb.append((minCost >= INF)? -1 : minCost).append("\\n");
}
System.out.println(sb);
}
static int[] numbers;
static boolean[] used;
private static void permutation(int size, int cnt) {
if (cnt == size){
calculateCost();
return;
}
for (int i = 0; i < size; i++){
if (used[i]) continue;
used[i] = true;
numbers[cnt] = i;
permutation(size, cnt+1);
used[i] = false;
}
}
static int minCost;
private static void calculateCost() {
int cost = startDist[numbers[0]];
for (int i = 0; i < numbers.length-1; i++){
cost += dist[numbers[i]][numbers[i+1]];
}
minCost = Math.min(minCost, cost);
}
static int[] dr = {-1, 1, 0, 0};
static int[] dc = {0, 0, -1, 1};
private static void getDist(Pos start, int[] dist) {
//bfs를 수행하여 구하기
Queue<Pos> q = new ArrayDeque<>();
boolean[][] visited = new boolean[H][W];
q.offer(start);
visited[start.r][start.c] = true;
while(!q.isEmpty()){
Pos cur = q.poll();
for (int di = 0; di < 4; di++){
int nr = cur.r + dr[di];
int nc = cur.c + dc[di];
if (nr < 0 || nr >= H || nc < 0 || nc >= W || visited[nr][nc] || map[nr][nc] == 'x') continue;
visited[nr][nc] = true;
if (intMap[nr][nc] != -1){
//더러운 곳임
dist[intMap[nr][nc]] = cur.depth + 1;
}
q.offer(new Pos(nr, nc, cur.depth+1));
}
}
}
}