|
| 1 | +import java.io.BufferedReader; |
| 2 | +import java.io.IOException; |
| 3 | +import java.io.InputStreamReader; |
| 4 | +import java.util.*; |
| 5 | + |
| 6 | +/* |
| 7 | + * 팩맨 게임: 몬스터의 복제, 이동, 팩맨의 이동, 시체 소멸, 몬스터 부화 단계를 구현 |
| 8 | + */ |
| 9 | +public class st_팩맨 { |
| 10 | + static int M, T; // 몬스터 수, 턴 수 |
| 11 | + static final int N = 4; // 격자 크기 (4x4) |
| 12 | + static Monster pacMan; |
| 13 | + static Queue<Monster> active = new ArrayDeque<>(); |
| 14 | + static Queue<Monster> eggs = new ArrayDeque<>(); |
| 15 | + static HashMap<Integer, Monster> liveInfo = new HashMap<>(); |
| 16 | + static List<List<Integer>> board = new ArrayList<>(); |
| 17 | + static int[] ghost = new int[N * N]; // 시체 시간 기록 |
| 18 | + static int idx = 0; // 몬스터 ID |
| 19 | + static int time; // 현재 턴 |
| 20 | + static int[] dx = {-1, -1, 0, 1, 1, 1, 0, -1}; |
| 21 | + static int[] dy = {0, -1, -1, -1, 0, 1, 1, 1}; |
| 22 | + static int mx; // 팩맨이 먹은 최대 몬스터 수 |
| 23 | + static Monster tmpPac; // 팩맨 임시 위치 |
| 24 | + static Set<Integer> eatMon; // 팩맨이 먹은 몬스터 위치 |
| 25 | + |
| 26 | + |
| 27 | + // 몬스터 복제 시도 |
| 28 | + private static void duplicate() { |
| 29 | + for (Monster mon : liveInfo.values()) { |
| 30 | + eggs.offer(new Monster(++idx, mon.r, mon.c, mon.d)); |
| 31 | + } |
| 32 | + } |
| 33 | + |
| 34 | + // 몬스터 이동 |
| 35 | + private static void moveMonsters() { |
| 36 | + int pacPos = getPos(pacMan.r, pacMan.c); // 팩맨 위치 |
| 37 | + int size = active.size(); |
| 38 | + |
| 39 | + for (int i = 0; i < size; i++) { |
| 40 | + Monster cur = active.poll(); |
| 41 | + int curPos = getPos(cur.r, cur.c); |
| 42 | + boolean moved = false; |
| 43 | + |
| 44 | + for (int j = 0; j < 8; j++) { |
| 45 | + int nd = (cur.d + j) % 8; |
| 46 | + int nr = cur.r + dx[nd]; |
| 47 | + int nc = cur.c + dy[nd]; |
| 48 | + int newPos = getPos(nr, nc); |
| 49 | + |
| 50 | + if (isValid(nr, nc) && newPos != pacPos && ghost[newPos] == 0) { |
| 51 | + cur.move(nr, nc, nd); |
| 52 | + board.get(curPos).remove(Integer.valueOf(cur.idx)); |
| 53 | + board.get(newPos).add(cur.idx); |
| 54 | + active.offer(cur); |
| 55 | + moved = true; |
| 56 | + break; |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + if (!moved) { // 움직이지 않았으면 다시 활동큐에 넣기 |
| 61 | + active.offer(cur); |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + // 팩맨 이동 |
| 67 | + private static void movePac() { |
| 68 | + mx = -1; |
| 69 | + tmpPac = new Monster(0, pacMan.r, pacMan.c, 0); |
| 70 | + |
| 71 | + for (int i = 0; i < 8; i += 2) { |
| 72 | + List<Integer> tmp = new ArrayList<>(); |
| 73 | + Monster tPac = new Monster(0, pacMan.r, pacMan.c, i); |
| 74 | + dfs(0, tPac, tmp, 0); |
| 75 | + } |
| 76 | + |
| 77 | + for (int pos : eatMon) { |
| 78 | + List<Integer> lst = board.get(pos); |
| 79 | + Iterator<Integer> itr = lst.iterator(); |
| 80 | + while (itr.hasNext()) { |
| 81 | + int mon = itr.next(); |
| 82 | + liveInfo.remove(mon); |
| 83 | + ghost[pos] = time; |
| 84 | + itr.remove(); |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | + pacMan.move(tmpPac.r, tmpPac.c, tmpPac.d); |
| 89 | + } |
| 90 | + |
| 91 | + // DFS로 팩맨의 최적 경로 탐색 |
| 92 | + private static void dfs(int depth, Monster tPac, List<Integer> tmp, int cnt) { |
| 93 | + if (depth == 3) { |
| 94 | + if (cnt > mx) { |
| 95 | + mx = cnt; |
| 96 | + eatMon = new HashSet<>(tmp); // 최종 먹은 몬스터 위치 저장 |
| 97 | + tmpPac = new Monster(tPac.idx, tPac.r, tPac.c, tPac.d); |
| 98 | + } |
| 99 | + return; |
| 100 | + } |
| 101 | + |
| 102 | + int od = tPac.d; // 원래 방향 저장 |
| 103 | + for (int i = 0; i < 8; i += 2) { |
| 104 | + int nr = tPac.r + dx[i]; |
| 105 | + int nc = tPac.c + dy[i]; |
| 106 | + if (!isValid(nr, nc)) continue; |
| 107 | + |
| 108 | + int pos = getPos(nr, nc); |
| 109 | + int addCnt = 0; |
| 110 | + |
| 111 | + if (!tmp.contains(pos)) { // 중복된 위치는 다시 먹지 않음 |
| 112 | + addCnt = board.get(pos).size(); |
| 113 | + if (addCnt > 0) tmp.add(pos); // 먹은 곳의 위치 추가 |
| 114 | + } |
| 115 | + |
| 116 | + tPac.move(nr, nc, i); // 팩맨 이동 |
| 117 | + dfs(depth + 1, tPac, tmp, cnt + addCnt); |
| 118 | + tPac.move(tPac.r - dx[i], tPac.c - dy[i], od); // 팩맨 위치 복구 |
| 119 | + |
| 120 | + if (addCnt > 0) tmp.remove(tmp.size() - 1); // 방문 기록 복구 |
| 121 | + } |
| 122 | + } |
| 123 | + |
| 124 | + |
| 125 | + // 시체 소멸 |
| 126 | + private static void clearGhosts() { |
| 127 | + for (int i = 0; i < N * N; i++) { |
| 128 | + if (time - ghost[i] > 2) { |
| 129 | + ghost[i] = 0; |
| 130 | + } |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | + // 몬스터 부화 |
| 135 | + private static void hatchingEggs() { |
| 136 | + while (!eggs.isEmpty()) { |
| 137 | + Monster mon = eggs.poll(); |
| 138 | + liveInfo.put(mon.idx, mon); |
| 139 | + board.get(getPos(mon.r, mon.c)).add(mon.idx); |
| 140 | + active.offer(mon); |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + // 유효한 위치 확인 |
| 145 | + private static boolean isValid(int r, int c) { |
| 146 | + return 0 <= r && r < N && 0 <= c && c < N; |
| 147 | + } |
| 148 | + |
| 149 | + // 2차원 좌표를 1차원 인덱스로 변환 |
| 150 | + private static int getPos(int r, int c) { |
| 151 | + return r * N + c; |
| 152 | + } |
| 153 | + |
| 154 | + public static void main(String[] args) throws IOException { |
| 155 | + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); |
| 156 | + StringTokenizer st = new StringTokenizer(br.readLine()); |
| 157 | + |
| 158 | + M = Integer.parseInt(st.nextToken()); |
| 159 | + T = Integer.parseInt(st.nextToken()); |
| 160 | + |
| 161 | + st = new StringTokenizer(br.readLine()); |
| 162 | + int pr = Integer.parseInt(st.nextToken()) - 1; |
| 163 | + int pc = Integer.parseInt(st.nextToken()) - 1; |
| 164 | + pacMan = new Monster(0, pr, pc, 0); |
| 165 | + |
| 166 | + // 보드 초기화 |
| 167 | + for (int i = 0; i < N * N; i++) { |
| 168 | + board.add(new ArrayList<>()); |
| 169 | + } |
| 170 | + |
| 171 | + for (int i = 0; i < M; i++) { |
| 172 | + st = new StringTokenizer(br.readLine()); |
| 173 | + int r = Integer.parseInt(st.nextToken()) - 1; |
| 174 | + int c = Integer.parseInt(st.nextToken()) - 1; |
| 175 | + int d = Integer.parseInt(st.nextToken()) - 1; |
| 176 | + |
| 177 | + Monster monster = new Monster(++idx, r, c, d); |
| 178 | + liveInfo.put(idx, monster); |
| 179 | + active.offer(monster); |
| 180 | + board.get(getPos(r, c)).add(monster.idx); |
| 181 | + } |
| 182 | + |
| 183 | + time = 1; |
| 184 | + while (T-- > 0) { |
| 185 | + // 1. 몬스터 복제 시도 |
| 186 | + duplicate(); |
| 187 | + |
| 188 | + // 2. 몬스터 이동 |
| 189 | + moveMonsters(); |
| 190 | + |
| 191 | + // 3. 팩맨 이동 |
| 192 | + movePac(); |
| 193 | + |
| 194 | + // 4. 시체 소멸 |
| 195 | + clearGhosts(); |
| 196 | + |
| 197 | + // 5. 몬스터 부화 |
| 198 | + hatchingEggs(); |
| 199 | + |
| 200 | + time++; |
| 201 | + } |
| 202 | + |
| 203 | + System.out.println(liveInfo.size()); |
| 204 | + } |
| 205 | + |
| 206 | + static class Monster { |
| 207 | + int idx, r, c, d; |
| 208 | + |
| 209 | + Monster(int idx, int r, int c, int d) { |
| 210 | + this.idx = idx; |
| 211 | + this.r = r; |
| 212 | + this.c = c; |
| 213 | + this.d = d; |
| 214 | + } |
| 215 | + |
| 216 | + void move(int r, int c, int d) { |
| 217 | + this.r = r; |
| 218 | + this.c = c; |
| 219 | + this.d = d; |
| 220 | + } |
| 221 | + |
| 222 | + @Override |
| 223 | + public String toString() { |
| 224 | + return "Monster{" + |
| 225 | + "idx=" + idx + |
| 226 | + ", r=" + r + |
| 227 | + ", c=" + c + |
| 228 | + ", d=" + d + |
| 229 | + '}'; |
| 230 | + } |
| 231 | + } |
| 232 | +} |
0 commit comments