[BOJ 21611] 마법사 상어와 블리자드
업데이트:
문제
- BOJ 21611
- 문제의 저작권은 Baekjoon Online Judge에 있습니다.
접근방식
이 문제에서 놓치기 쉬운 부분은 리스트로 풀면 중간에 구슬을 이동하면서 리스트를 만들고, 구슬을 파괴하는 과정에서 구슬이 없는 경우, 즉 리스트의 사이즈가 0이 되는 경우가 발생할 수도 있다는 것이다. 따라서 중간중간에 진행하면서 list
가 비었으면 종료해주어야한다.
또한, map은 구슬파괴할때나 이동할때만 필요하고 이후에 파괴,그룹만들기 부터는 굳이 map에서 달팽이이동을 해 줄 필요가 없다. 구슬을 앞으로 당기기 때문에 이는 리스트에서 작업하는게 코드 작성하기 쉽다.
- 구슬 파괴
breaking()
- 상어좌표로 부터
s
만큼 현재 방향d
로 가면서map[][]
값을 초기화해준다.
- 상어좌표로 부터
- 구슬 이동
moving()
- 상어좌표로 부터 이동(달팽이)을 하면서 구슬값을
list
에 넣어줌으로써 앞으로 당기는 작업을 한다.
- 상어좌표로 부터 이동(달팽이)을 하면서 구슬값을
- 구슬 파괴
explode()
- 구슬이 폭발할 때까지 진행하므로 폭발했는지를 저장할
flag
변수를 만든다. list
를 돌면서i
번째 구슬과i+1
번째 구슬이 같으면 연속한 개수를 저장한다. 개수cnt
가 4보다 작을 경우 새로운 리스트tmpList
에 넣고 더 크다면 넣지 않는다.- 폭발가능하면 그때의 구슬 번호
now
를Ans[now]
에 cnt수만큼 누적한다. flag
가false
가 될 때 까지 반복한다.
- 구슬이 폭발할 때까지 진행하므로 폭발했는지를 저장할
- 구슬 그룹 넣기
makeGroup()
list
를 돌면서i
번째 구슬과i+1
번째 구슬이 같으면 연속한 개수cnt
를 카운트하고 새로운 리스트tmpList
에cnt
와 구슬번호now
를 차례대로 넣는다.- 그룹을 만들었으면 다시
map
에 구슬리스트를 넣어주기 위해updateMap
에서 달팽이이동을 리스트 개수만큼 차례대로 넣어준다.
모든 수행이 끝나면 Ans
의 0부터 3까지 값의 합을 출력해주면된다.
import java.io.*;
import java.util.*;
public class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer st;
static int N, M;
static int mid; // 마법사 상어의 위치
static int d, s; // 방향, 속력
static List<Integer> list;
static int[][] dArr = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
static int[][] snailDrr = { { 0, -1 }, { 1, 0 }, { 0, 1 }, { -1, 0 } };
static int[][] map;
static int nowX, nowY, nowD; // 구슬 이동할 때 좌표
static int[] Ans;
public static void main(String[] args) throws IOException {
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
map = new int[N + 1][N + 1];
mid = (N + 1) / 2;
Ans = new int[4];
for (int i = 1; i <= N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 1; j <= N; j++) {
map[i][j] = Integer.parseInt(st.nextToken());
}
}
for (int m = 0; m < M; m++) {
st = new StringTokenizer(br.readLine());
d = Integer.parseInt(st.nextToken()) - 1;
s = Integer.parseInt(st.nextToken());
// 1. 구슬 파괴
breaking();
// 2. 구슬 이동: 일렬로 리스트 만들기
moving();
if(list.size() == 0) break;
// 3. 구슬 폭발: 연속하는 구슬 없애기
while(true) {
if(!explode()) break;
}
if(list.size() == 0) break;
// 4. 그룹 만들기
makeGroup();
}
System.out.println(Ans[1]*1+Ans[2]*2+Ans[3]*3);
}
private static void makeGroup() {
List<Integer> tmpList = new ArrayList<>(); // 구슬개수, 번호 저장
int i = 0;
while (i < list.size()) {
int now = list.get(i);
if (i == list.size() - 1) { // 마지막 하나 남았으면 종료
tmpList.add(1);
tmpList.add(now);
break;
}
int next = list.get(i + 1); // 다음 좌표
if (now == next) { // 연속하면 총 몇개인지 세기
int cnt = 1;
int j = i;
while (j < list.size() - 1) {
if (list.get(j) == list.get(j + 1)) {
cnt++;
j++;
} else break;
}
tmpList.add(cnt);
tmpList.add(now);
i = j + 1;
} else { // 한개면
tmpList.add(1);
tmpList.add(now);
i++;
}
}
list = tmpList;
updateMap(); //리스트정보->맵에 갱신
}
private static void updateMap() {
int nx = mid;
int ny = mid;
int d = 0; // 현재 방향
int dist = 1;
boolean flag = true;
int i = 0;
map = new int[N + 1][N + 1]; // map초기화
while (flag) {
for (int cnt = 0; cnt < 2; cnt++) { // 2번씩 반복
for (int go = 0; go < dist; go++) { // 방향 바뀌는 시점
nx += snailDrr[d][0];
ny += snailDrr[d][1];
map[nx][ny] = list.get(i++); // 새 공을 넣어줌
if (nx == 1 && ny == 1) return;
if (i >= list.size()) return;
}
d= (d+ 1) % 4; // 방향 바꿈
}
dist++;
}
}
private static boolean explode() {
boolean flag = false; // 폭발했는지
List<Integer> tmpList = new ArrayList<>();
int i = 0;
while (list.size() > 0 && i < list.size()) {
if (i == list.size() - 1) { // 마지막 하나 남았으면 종료
tmpList.add(list.get(i));
break;
}
int now = list.get(i);
int next = list.get(i + 1);
if (now == next) { // 연속하면 총 몇개인지 세기
int cnt = 1;
int j = i;
while (j < list.size() - 1) {
if (list.get(j) == list.get(j + 1)) {
cnt++;
j++;
} else
break;
}
if (cnt < 4) for (int k = 0; k < cnt; k++) tmpList.add(now); // 4개 이상이면 안넣음
else {
flag = true; // 폭발했음
Ans[now] += cnt;
}
i = j + 1;
} else {
tmpList.add(now);
i++;
}
}
list = tmpList;
return flag;
}
private static void moving() {
int nx = mid;
int ny = mid;
int d = 0; // 현재 방향
int dist = 1;
list = new ArrayList<>();
while (true) {
for (int cnt = 0; cnt < 2; cnt++) { // 2번씩 반복
for (int go = 0; go < dist; go++) { // 방향 바뀌는 시점
nx += snailDrr[d][0];
ny += snailDrr[d][1];
if (map[nx][ny] > 0) list.add(map[nx][ny]);
if (nx == 1 && ny == 1) return;
}
d = (d + 1) % 4; // 방향 바꿈
}
dist++;
}
}
private static void breaking() {
int nx = mid;
int ny = mid;
for(int i=0; i<s; i++) {
nx += dArr[d][0];
ny += dArr[d][1];
if (nx >= 1 && nx <= N && ny >= 1 && ny <= N) map[nx][ny] = 0; // 구슬 파괴
}
}
}