문제 링크 ▶︎ 백준 스타트 택시 19238
문제를 보자마자 어떻게 풀지 막막했는데 먼저 해야할 것은 택시가 어떤 승객을 태우러 가는지에 대한 로직이 필요하다고 생각했다.
그리고 승객을 태우고 나서 목적지에 가는 로직도 필요하다. 즉, 최단거리를 탐색하는 과정이 승객을 태우러 갈때 한번, 승객을 태우고 목적지로 갈 때 한번, 총 2번의 최단 거리 탐색이 필요하다고 생각했다.
여러명의 승객이 택시에서 동일한 거리의 위치에 존재한다면 제일 위, 제일 왼쪽의 순서로 태워야한다는 조건으로 인해서 dir의 순서로 처리할 수 있을 거라 생각했는데 반례가 존재해서 따로 승객의 우선 순위를 찾는 로직도 필요하다.
걍 어렵다.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken()); // 승객 수
int fuel = Integer.parseInt(st.nextToken()); // 연료
int[][] road = new int[n][n];
for (int i = 0; i < n; i++) {
String[] tokens = br.readLine().split(" ");
for (int j = 0; j < n; j++) {
road[i][j] = Integer.parseInt(tokens[j]);
}
}
int[][] start = new int[m][2];
int[][] end = new int[m][2];
st = new StringTokenizer(br.readLine());
int taxi_y = Integer.parseInt(st.nextToken())-1;
int taxi_x = Integer.parseInt(st.nextToken())-1;
for (int i = 0; i < m; i++) {
st = new StringTokenizer(br.readLine());
start[i][0] = Integer.parseInt(st.nextToken())-1;
start[i][1] = Integer.parseInt(st.nextToken())-1;
end[i][0] = Integer.parseInt(st.nextToken())-1;
end[i][1] = Integer.parseInt(st.nextToken())-1;
}
HashSet<Integer> done = new HashSet<>();
while(done.size() < m){
int[] out = next(taxi_y, taxi_x, start, road, done);
if(out[0] == -1){
fuel = -1;
break;
}
int idex = out[0];
int consumed = out[1];
fuel -= consumed;
if(fuel < 0){
fuel = -1;
break;
}
int dist = distance(start[idex][0],start[idex][1],end[idex][0],end[idex][1],road);
if(dist == -1 || fuel < dist){
fuel = -1;
break;
}
done.add(idex);
taxi_y = end[idex][0];
taxi_x = end[idex][1];
fuel += dist;
}
System.out.println(fuel);
}
public static int distance(int before_y, int before_x, int after_y, int after_x, int[][] road) {
int n = road.length;
int[][] dir = {{-1,0},{0,-1},{1,0},{0,1}};
boolean[][] visit = new boolean[n][n];
Queue<int[]> q = new ArrayDeque<>();
q.add(new int[]{before_y, before_x,0});
visit[before_y][before_x] = true;
while (!q.isEmpty()) {
int[] curr = q.remove();
if (curr[0] == after_y && curr[1] == after_x) {
return curr[2];
}
for (int[] d : dir) {
int ny = curr[0] + d[0];
int nx = curr[1] + d[1];
if (ny >= 0 && ny < n && nx >= 0 && nx < n && road[ny][nx] == 0 && !visit[ny][nx]) {
visit[ny][nx] = true;
q.add(new int[]{ny, nx, curr[2]+1});
}
}
}
return -1;
}
public static int[] next(int y, int x, int[][] start, int[][] road, HashSet<Integer> done) {
int n = road.length;
int[][] dir = {{-1,0},{0,-1},{1,0},{0,1}};
boolean[][] visit = new boolean[n][n];
Queue<int[]> q = new ArrayDeque<>();
q.add(new int[]{y, x, 0});
visit[y][x] = true;
List<int[]> box = new ArrayList<>();
int min_dist = Integer.MAX_VALUE;
while (!q.isEmpty()) {
int[] curr = q.remove();
if(curr[2] > min_dist) {
break;
}
for(int i = 0; i < start.length; i++){
if(!done.contains(i) && curr[0] == start[i][0] && curr[1] == start[i][1]){
box.add(new int[]{i, curr[2]});
min_dist = curr[2];
}
}
for (int[] d : dir) {
int ny = curr[0] + d[0];
int nx = curr[1] + d[1];
if(ny >= 0 && ny < n && nx >= 0 && nx < n && road[ny][nx] == 0 && !visit[ny][nx]) {
visit[ny][nx] = true;
q.add(new int[]{ny, nx, curr[2]+1});
}
}
}
if(box.size() == 0){
return new int[]{-1, -1};
}
box.sort((a, b) -> {
if(start[a[0]][0] != start[b[0]][0]){ // start[idx][0] -> row
return start[a[0]][0] - start[b[0]][0];
}
return start[a[0]][1] - start[b[0]][1];
});
return box.get(0);
}
}
2시간 정도를 푼 것 같은데 조건이 굉장히 많아서 까다로웠다. 이 코드를 누군가 보고 이해할 수 있을까.. 너무 더러운 코드를 짠 것 같지만 여튼 풀어서 만족한다.
처음부터 조건을 정리하면서 풀었다면 풀이 시간을 줄일 수 있었을 것 같다. 상좌를 순서로 승객을 태우는 조건이나 최단거리에 해당하는 승객을 발견했으면 그 거리를 비교하는데 쓰는 것도 충분히 미리 생각할 수 있었는데 시간초과에 걸리자 시간을 줄이기 위해서 생각해보니 떠올랐다.
조건이 복잡한 문제일수록 손코딩에 시간을 투자하고 정리를 좀 하고 풀어야겠다.