Live Today
[코드트리] 미로 타워 디펜스 (Java) 본문
반응형
코드트리 | 코딩테스트 준비를 위한 알고리즘 정석
국가대표가 만든 코딩 공부의 가이드북 코딩 왕초보부터 꿈의 직장 코테 합격까지, 국가대표가 엄선한 커리큘럼으로 준비해보세요.
www.codetree.ai
✔️ 문제 리뷰
- 하........도대체 왜 오래 걸렸을까.......
- monster 삭제하는 부분에서 가장 마지막 배열 쪽에 연속되는 monster 4개일 때를 삭제하는 로직이 없어서 시간초과가 계속 발생했었던 것임.
✔️ 문제 풀이
- 입력 받기 전, 2차원 배열의 중앙 칸부터 나선형으로 순서를 numBoard에 미리 세팅해둔다.
- 입력받은 공격 방향 d와 공격 칸 수 p가 주어진다.
- 주어진 방향과 칸의 수만큼 monster를 공격한다.
- monster의 종류가 4번 이상 같으면 해당 monster들 삭제한다. 단, 삭제 작업은 한 번에 이루어진다.
- 4번 이상 나오는 monster가 없을 때까지 반복해준다.
- 삭제가 끝난 후, monster의 (총 개수, monster 종류) 쌍으로 다시 미로에 넣어준다.
- 입력이 끝난 후, 삭제된 monster들의 총합을 출력한다.
✅ 정답 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
static int N,M,result;
static int[][] numBoard;
static int[][] monsterBoard;
// → ↓ ← ↑
static int[] dx = {0,1,0,-1};
static int[] dy = {1,0,-1,0};
static ArrayList<Monster> monsterList;
static class Monster {
int num;
public Monster(int num) {
this.num = num;
}
}
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
numBoard = new int[N][N];
monsterList = new ArrayList<>(); // Monster List
monsterBoard = new int[N][N];
for(int i=0;i<N;i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<N;j++) {
int num = Integer.parseInt(st.nextToken());
if(num > 0)
monsterBoard[i][j] = num;
}
}
init_list();
for(int i=0;i<M;i++) {
st = new StringTokenizer(br.readLine());
int d = Integer.parseInt(st.nextToken());
int p = Integer.parseInt(st.nextToken());
solve(d,p);
}
System.out.println(result);
}
private static void init_list() {
int tx = N / 2;
int ty = N / 2;
numBoard[tx][ty] = -1;
int d = 0;
int cnt = 1;
int round = 0;
// 왼 - 아래 - 오 - 위
int[] ddx = {0,1,0,-1};
int[] ddy = {-1,0,1,0};
int idx = 0;
outer:while(is_valid(tx, ty)) {
for(int i=0;i<cnt;i++) {
tx += ddx[d];
ty += ddy[d];
if(!is_valid(tx,ty)) break outer;
if(is_valid(tx,ty) && monsterBoard[tx][ty] == 0) {
numBoard[tx][ty] = idx++;
continue;
}
numBoard[tx][ty] = idx++;
monsterList.add(new Monster(monsterBoard[tx][ty]));
}
d = change_dir(d);
round++;
if(round % 2 == 0) cnt++;
}
}
private static void solve(int d, int p) {
// 1. 플레이어는 상하좌우 방향 중 주어진 공격 칸 수만큼 몬스터를 공격하여 없앨 수 있습니다.
// 2. 비어있는 공간만큼 몬스터는 앞으로 이동하여 빈 공간을 채웁니다.
attack_monster(d,p);
// 3. 이때 몬스터의 종류가 4번 이상 반복하여 나오면 해당 몬스터 또한 삭제됩니다. 해당 몬스터들은 동시에 사라집니다.
// 삭제된 이후에는 몬스터들을 앞으로 당겨주고, 4번 이상 나오는 몬스터가 있을 경우 또 삭제를 해줍니다. 4번 이상 나오는 몬스터가 없을 때까지 반복해줍니다.
while(is_possible())
remove_monster();
// 삭제가 끝난 다음에는 몬스터를 차례대로 나열했을 때 같은 숫자끼리 짝을 지어줍니다.
// 이후 각각의 짝을 (총 개수, 숫자의 크기)로 바꾸어서 다시 미로 속에 집어넣습니다.
make_set();
update_monsterBoard();
}
private static void update_monsterBoard() {
monsterBoard = new int[N][N];
int tx = N / 2;
int ty = N / 2;
int d = 0;
int cnt = 1;
int round = 0;
// 왼 - 아래 - 오 - 위
int[] ddx = {0,1,0,-1};
int[] ddy = {-1,0,1,0};
outer:while(is_valid(tx, ty)) {
for(int i=0;i<cnt;i++) {
tx += ddx[d];
ty += ddy[d];
if(!is_valid(tx,ty) || numBoard[tx][ty] >= monsterList.size()) break outer;
monsterBoard[tx][ty] = monsterList.get(numBoard[tx][ty]).num;
}
d = change_dir(d);
round++;
if(round % 2 == 0) cnt++;
}
}
private static void make_set() {
ArrayList<Integer> renewalList = new ArrayList<>();
int number = monsterList.get(0).num;
int cnt = 1;
for(int i=1;i<monsterList.size();i++) {
if(number == monsterList.get(i).num) {
cnt++;
continue;
}
if(number != monsterList.get(i).num) {
renewalList.add(cnt);
renewalList.add(number);
cnt = 1;
number = monsterList.get(i).num;
}
}
renewalList.add(cnt);
renewalList.add(number);
monsterList.clear();
for(int i=0;i<renewalList.size();i++) {
int m = renewalList.get(i);
// 만약 새로 생긴 배열이 원래 격자의 범위를 넘는다면 나머지 배열은 무시합니다.
if(i > numBoard[0][0]) break;
monsterList.add(new Monster(m));
}
}
private static boolean is_possible() {
boolean flag = false;
int cnt = 1;
int temp = monsterList.get(0).num;
for(int i=1;i<monsterList.size();i++) {
if(cnt >= 4) return true;
if(temp == monsterList.get(i).num) {
cnt++;
continue;
}
if(temp != monsterList.get(i).num) {
cnt = 1;
temp = monsterList.get(i).num;
continue;
}
}
if(cnt >= 4) return true;
return flag;
}
private static void remove_monster() {
// 3. 이때 몬스터의 종류가 4번 이상 반복하여 나오면 해당 몬스터 또한 삭제됩니다. 해당 몬스터들은 동시에 사라집니다.
// 삭제된 이후에는 몬스터들을 앞으로 당겨주고, 4번 이상 나오는 몬스터가 있을 경우 또 삭제를 해줍니다. 4번 이상 나오는 몬스터가 없을 때까지 반복해줍니다.
Queue<int[]> q = new LinkedList<int[]>(); // {index, cnt}
int temp = monsterList.get(0).num;
int cnt = 1;
for(int i=1;i<monsterList.size();i++) {
if(temp == monsterList.get(i).num) {
cnt++;
continue;
}
if(temp != monsterList.get(i).num) {
if(cnt >= 4) {
q.add(new int[] {i-1,cnt});
}
cnt = 1;
temp = monsterList.get(i).num;
continue;
}
}
if(cnt >= 4) {
q.add(new int[] {monsterList.size()-1,cnt});
}
ArrayList<Monster> removeList = new ArrayList<>();
// 한 번에 삭제할 monster 정보
while(!q.isEmpty()) {
int[] tmp = q.poll();
int idx = tmp[0];
int count = tmp[1];
for(int i=0;i<count;i++) {
int num = monsterList.get(idx).num;
result += num;
removeList.add(monsterList.get(idx--));
}
}
monsterList.removeAll(removeList);
}
private static void attack_monster(int d, int p) {
int tx = N / 2;
int ty = N / 2;
ArrayList<Monster> removeList = new ArrayList<>();
for(int i=0;i<p;i++) {
tx += dx[d];
ty += dy[d];
if(monsterBoard[tx][ty] == 0) continue;
if(monsterBoard[tx][ty] > 0) {
monsterBoard[tx][ty] = 0;
result += monsterList.get(numBoard[tx][ty]).num;
removeList.add(monsterList.get(numBoard[tx][ty]));
}
}
monsterList.removeAll(removeList);
}
private static int change_dir(int d) {
if(d == 3) return 0;
return d+1;
}
private static boolean is_valid(int r, int c) {
if(r<0 || c<0 || r>=N || c>=N) return false;
return true;
}
}
'알고리즘 > 코드트리' 카테고리의 다른 글
[코드트리] 색깔 폭탄 (Java) (0) | 2023.09.07 |
---|---|
[코드트리] 회전하는 빙하 (Java) (0) | 2023.09.04 |
[코드트리] 술래잡기 체스 (Java) (0) | 2023.08.11 |
[코드트리] 원자 충돌 (Java) (0) | 2023.08.01 |
[코드트리] 놀이기구 탑승 (Java) (0) | 2023.07.25 |