Skip to content

Commit

Permalink
[feat] 게시물 업로드
Browse files Browse the repository at this point in the history
  • Loading branch information
piacu committed Mar 16, 2024
1 parent 3027243 commit aed9b7b
Showing 1 changed file with 262 additions and 0 deletions.
262 changes: 262 additions & 0 deletions _posts/2024-03-14-CODETREE-왕실의-기사-대결.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
---

layout: post
title: "[Java] 코드트리: 루돌프의 반란"
tags: [algorithm, codetree, java]

---

왕실의 기사들은 *L*×*L* 크기의 체스판 위에서 대결을 준비하고 있습니다. 체스판의 왼쪽 상단은 (1,1)로 시작하며, 각 칸은 빈칸, 함정, 또는 벽으로 구성되어 있습니다. 체스판 밖도 벽으로 간주합니다.

왕실의 기사들은 자신의 마력으로 상대방을 밀쳐낼 수 있습니다. 각 기사의 초기위치는 (*r*,*c*)로 주어지며, 그들은 방패를 들고 있기 때문에 (*r*,*c*)를 좌측 상단으로 하며 *h*(높이)×*w*(너비) 크기의 직사각형 형태를 띄고 있습니다. 각 기사의 체력은 *k*로 주어집니다.

**(1) 기사 이동**

왕에게 명령을 받은 기사는 상하좌우 중 하나로 한 칸 이동할 수 있습니다. 이때 만약 이동하려는 위치에 다른 기사가 있다면 그 기사도 함께 연쇄적으로 한 칸 밀려나게 됩니다. 그 옆에 또 기사가 있다면 연쇄적으로 한 칸씩 밀리게 됩니다. 하지만 만약 기사가 이동하려는 방향의 끝에 벽이 있다면 모든 기사는 이동할 수 없게 됩니다. 또, 체스판에서 사라진 기사에게 명령을 내리면 아무런 반응이 없게 됩니다.

**(2) 대결 대미지**

명령을 받은 기사가 다른 기사를 밀치게 되면, 밀려난 기사들은 피해를 입게 됩니다. 이때 각 기사들은 해당 기사가 이동한 곳에서 *w*×*h* 직사각형 내에 놓여 있는 함정의 수만큼만 피해를 입게 됩니다. 각 기사마다 피해를 받은 만큼 체력이 깎이게 되며, 현재 체력 이상의 대미지를 받을 경우 기사는 체스판에서 사라지게 됩니다. 단, 명령을 받은 기사는 피해를 입지 않으며, 기사들은 모두 밀린 이후에 대미지를 입게 됩니다. **밀렸더라도 밀쳐진 위치에 함정이 전혀 없다면 그 기사는 피해를 전혀 입지 않게 됨에 유의합니다.**

*Q* 번에 걸쳐 왕의 명령이 주어졌을 때, *Q* 번의 대결이 모두 끝난 후 생존한 기사들이 총 받은 대미지의 합을 출력하는 프로그램을 작성해보세요.

## 입력 형식

첫 번째 줄에 *L*, *N*, *Q*가 공백을 사이에 두고 주어집니다.

다음 *L* 개의 줄에 걸쳐서 *L*×*L* 크기의 체스판에 대한 정보가 주어집니다.

- 0이라면 빈칸을 의미합니다.
- 1이라면 함정을 의미합니다.
- 2라면 벽을 의미합니다.

다음 *N* 개의 줄에 걸쳐서 초기 기사들의 정보가 주어집니다. 이 정보는 (*r*,*c*,*h*,*w*,*k*) 순으로 주어지며, 이는 기사의 처음 위치는 (*r*,*c*)를 좌측 상단 꼭지점으로 하며 세로 길이가 *h*, 가로 길이가 *w*인 직사각형 형태를 띄고 있으며 초기 체력이 *k*라는 것을 의미합니다. 입력은 1번 기사부터 *N*번 기사까지 순서대로 정보가 주어집니다.

**단, 처음 주어지는 기사들의 위치는 기사들끼리 서로 겹쳐져 있지 않습니다. 또한 기사와 벽은 겹쳐서 주어지지 않습니다.**

다음 *Q* 개의 줄에 걸쳐 왕의 명령에 대한 정보가 주어집니다. 이 정보는 (*i*,*d*) 형태로 주어지며 이는 *i*번 기사에게 방향 *d*로 한 칸 이동하라는 명령임을 뜻합니다. *i*는 1 이상 *N* 이하의 값을 갖으며, 이미 사라진 기사 번호가 주어질 수도 있음에 유의합니다. *d*는 0, 1, 2, 3 중에 하나이며 각각 위쪽, 오른쪽, 아래쪽, 왼쪽 방향을 의미합니다.

- *L*: 체스판의 크기 (3≤*L*≤40)
- *N*: 기사의 수 (1≤*N*≤30)
- *Q*: 명령의 수 (1≤*Q*≤100)
- *k*: 기사의 체력 (1≤*k*≤100)

## 출력 형식

*Q* 개의 명령이 진행된 이후, 생존한 기사들이 총 받은 대미지의 합을 출력합니다.

## 풀이 방법

구현 + BFS(DFS도 가능) 문제

각 기사는 1x1 사이즈가 아닐 수도 있음. 직사각형 모양으로 주어진다.



**생각해봐야 하는 점**

1) 한 기사가 특정 방향 d 방향(상하좌우 중 1)으로 이동했을 때, 해당 위치에 다른 기사가 있다면 그 기사도 함께 d 방향으로 밀린다. 그 뒤에 기사가 있다면 똑같이 밀린다.
2) 벽을 만나면 밀리지 않는다. 주어진 *L*×*L* 크기의 체스판 바깥도 벽이라고 가정한다.

=> 1+2를 생각해봤을 때, i번 기사가 d 방향으로 이동할 때, i번 기사와 맞붙어있는 다른 기사들도 밀릴 수 있는 지 체크한 후(a), 해당 방향으로 이동하고 점수 계산(b) 하면 되겠다고 생각했다.



(a) checkWall 메소드 - BFS로 해결

1. Queue를 돌리기 위해 모든 기사들의 위치를 2차원 배열에 찍어주고, affected 배열을 만들어 영향을 받은(move메소드를 통해 이동해야 할) 기사들을 관리했다.

1. 현재 이동할 i번 기사의 위치 전체를 Queue에 담고, 방문 배열에다가 찍어줬다.
2. Queue를 실행해 d 방향으로 이동했을 때, 벽을 만나면 true(벽 있음)를 리턴, 이동하다가 j번 기사를 만나면 Queue에 모든 위치를 집어넣고, 방문 배열을 찍어줬다.
3. Queue가 끝날 때까지 벽을 만나지 않으면 false(벽 없음)를 리턴했다.



(b) move 메소드

1. affected 배열에서 이동 가능한 기사들을 뽑아내 이동시키고, 이동한 위치에 폭탄이 있는지 점검한다. *명령을 받은 기사는 피해 입지 않는다!!*
2. 폭탄이 있다면 데미지를 받게 된다.
3. 체력에서 데미지를 빼주고, 0 이하라면 사망 처리를 한다.
4. 이동한 기사들의 위치를 d방향으로 한 칸씩 민다.



걸린 시간: 3시간

## 풀이 코드

```java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
static class Knight {
int r;
int c;
int h;
int w;
int health;
boolean isDead;
int damage;

public Knight() {}
public Knight(int r, int c, int h, int w, int health) {
this.r = r;
this.c = c;
this.h = h;
this.w = w;
this.health = health;
}
@Override
public String toString() {
return "Knight [r=" + r + ", c=" + c + ", h=" + h + ", w=" + w + ", health=" + health + ", isDead=" + isDead
+ ", damage=" + damage + "]";
}

}
static int[][] MAP;
static final int[] dr = {-1,0,1,0}; // 상 우 하 좌
static final int[] dc = {0,1,0,-1};
static int L;
static List<Knight> knights;
static boolean[] affected;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
L = Integer.parseInt(st.nextToken());
int N = Integer.parseInt(st.nextToken());
int Q = Integer.parseInt(st.nextToken());
MAP = new int[L+1][L+1];

for(int i=1; i<=L; ++i) {
st = new StringTokenizer(br.readLine());
for(int j=1; j<=L; ++j) {
MAP[i][j] = Integer.parseInt(st.nextToken());
}
}

knights = new ArrayList<>();
knights.add(new Knight()); // 0번 인덱스
for(int i=0; i<N; ++i) {
st = new StringTokenizer(br.readLine());
int r = Integer.parseInt(st.nextToken());
int c = Integer.parseInt(st.nextToken());
int h = Integer.parseInt(st.nextToken());
int w = Integer.parseInt(st.nextToken());
int k = Integer.parseInt(st.nextToken());
knights.add(new Knight(r, c, h, w, k));
}

while(Q-- > 0) {
st = new StringTokenizer(br.readLine());
int knightNum = Integer.parseInt(st.nextToken());
int dir = Integer.parseInt(st.nextToken());

if(!checkWall(knightNum, dir)) {
move(knightNum, dir);
}
}

int ans = 0;
for(int i=1; i<=N; ++i) {
if(!knights.get(i).isDead) {
ans += knights.get(i).damage;
}
}

System.out.println(ans);
}
private static void move(int knightNum, int dir) {
for(int n=1; n<affected.length; ++n) { // 폭탄 개수 세기
if(!affected[n] || n == knightNum) continue;
Knight k = knights.get(n);

for(int i=k.r; i<k.r+k.h; ++i) {
for(int j=k.c; j<k.c+k.w; ++j) {
int nr = i + dr[dir];
int nc = j + dc[dir];

if(MAP[nr][nc] == 1)
++k.damage;
}
}
}

for(int i=1; i<knights.size(); ++i) { // 기사 위치 및 체력 최신화
if(affected[i]) {
Knight k = knights.get(i);
k.r += dr[dir];
k.c += dc[dir];

if(i == knightNum) continue;

if(k.health <= k.damage) {
k.isDead = true;
}
}
}
}
private static boolean checkWall(int knightNum, int dir) {
if(knights.get(knightNum).isDead) return true;

int[][] knightMap = new int[L+1][L+1];

for(int n=1; n<knights.size(); ++n) { // 기사 위치 맵 만들기
Knight k = knights.get(n);

if(k.isDead) continue;

for(int i=k.r; i<k.r+k.h; ++i) {
for(int j=k.c; j<k.c+k.w; ++j) {
knightMap[i][j] = n;
}
}
}

Knight k = knights.get(knightNum);
Queue<int[]> q = new LinkedList<>();
affected = new boolean[knights.size()];
boolean[][] v = new boolean[L+1][L+1];

for(int i=k.r; i<k.r+k.h; ++i) { // 현재 기사 위치 목록 queue에 담기
for(int j=k.c; j<k.c+k.w; ++j) {
q.offer(new int[] {i, j});
v[i][j] = true;
}
}
affected[knightNum] = true;

while(!q.isEmpty()) {
int[] poll = q.poll();

int nr = poll[0] + dr[dir];
int nc = poll[1] + dc[dir];

// 범위 바깥 또는 벽과 마주치면 true 리턴(벽 있음)
if(nr<1 || nr>L || nc<1 || nc>L || MAP[nr][nc] == 2) return true;
else if(knightMap[nr][nc] != knightNum && !v[nr][nc]) { // 새로운 기사 만나면
k = knights.get(knightMap[nr][nc]);

for(int i=k.r; i<k.r+k.h; ++i) {
for(int j=k.c; j<k.c+k.w; ++j) {
q.offer(new int[] {i, j});
affected[knightMap[nr][nc]] = true;
v[i][j] = true;
}
}
}
}

return false;
}
}
```



#왕실의기사대결 #코드트리 #자바 #java #알고리즘 #ps

0 comments on commit aed9b7b

Please sign in to comment.