문제
3025번: 돌 던지기 (acmicpc.net)
해결방법
- 초기 아이디어
- 시간을 줄이기 위한 아이디어
- DP 아이디어
- 첫번째 행에 대해 특정 열에서 떨어뜨린 돌이 도착한 곳의 직전 위치를 (dp[R][0], dp[R][1]) 으로 저장하고 이후 똑같은 행에서 떨어뜨릴 경우 해당 위치로 이동
- 다른 위치에서 떨어진 돌에 의해 경로가 변경되어야 할 가능성 존재
- 특정 열에서 돌이 떨어지는 경로를 저장하고 해당 경로들을 pop 하면서 돌이 없는 곳을 찾아서 거기서 부터 시뮬레이션
동작 과정
- 처음 돌을 떨어뜨릴 위치의 값을 c 라고 한다.
- c를 통해 돌이 떨어지는 경로를 저장할 Stack을 C 만큼 배열로 준비한다.
- 스택이 비어있는 상태 ( c의 위치에서 돌을 처음 던지는 경우) 일때는 0, c 에서 돌던지기를 실행한다.
- 돌 던지기를 실행하며 돌이 이동하는 경로를 c에 해당하는 stack에 추가한다.
- 이전에 돌을 던졌던 위치 c 에서 다시 돌 던지기를 수행하면 이전에 떨어졋던 곳 부터 경로를 역추적하여 빈 곳을 찾는다.
- 스택을 통해 찾은 다음 돌이 떨어질 곳 부터 drop 을 수행한다.
코드
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Stack;
import java.util.StringTokenizer;
public class Main {
static int R, C;
static char[][] map;
static Stack<Point>[] dp;
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
StringTokenizer st = new StringTokenizer(in.readLine());
R = Integer.parseInt(st.nextToken());
C = Integer.parseInt(st.nextToken());
map = new char[R + 1][C + 1];
dp = new Stack[C + 1];
for(int i = 1; i <= C; i++)
dp[i] = new Stack<>();
for (int i = 1; i <= R; i++) {
String s = in.readLine();
for (int j = 1; j <= C; j++) {
map[i][j] = s.charAt(j - 1);
}
}
int N = Integer.parseInt(in.readLine());
for (int i = 0; i < N; i++) {
int c = Integer.parseInt(in.readLine());
while(!dp[c].isEmpty() && map[dp[c].peek().y][dp[c].peek().x] == 'O')
dp[c].pop();
if(dp[c].isEmpty())
drop(1, c, c);
else
drop(dp[c].peek().y, dp[c].peek().x, c);
}
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++) {
out.write(map[i][j]);
}
out.write("\n");
}
out.flush();
}
static void drop(int y, int x, int c) {
while (y + 1 <= R && map[y + 1][x] != 'X') {
if (map[y + 1][x] == 'O') {
if (isInBound(y + 1, x - 1) && map[y + 1][x - 1] == '.' && map[y][x-1] == '.') {
y++;
x--;
}
else if (isInBound(y + 1, x + 1) && map[y + 1][x + 1] == '.' && map[y][x+1] == '.') {
y++;
x++;
}
else
break;
}
else
y++;
dp[c].push(new Point(y, x));
}
map[y][x] = 'O';
}
static boolean isInBound(int y, int x) {
if (y > 0 && y <= R && x > 0 && x <= C)
return true;
else
return false;
}
static class Point{
int y;
int x;
public Point(int y, int x) {
this.y = y;
this.x = x;
}
}
}