-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
--- | ||
|
||
layout: post | ||
title: "[Java] 코드트리: 포탑 부수기" | ||
tags: [algorithm, codetree, java, 코드트리, 포탑부수기] | ||
|
||
--- | ||
|
||
*N*×*M* 격자가 있고, 모든 위치에는 포탑이 존재합니다. (즉, 포탑의 개수는 *NM*개) | ||
|
||
각 포탑에는 공격력이 존재하며, 상황에 따라 공격력이 줄어들거나 늘어날 수 있습니다. 또한, 공격력이 0 이하가 된다면, 해당 포탑은 부서지며 더 이상의 공격을 할 수 없습니다. 최초에 공격력이 0인 포탑 즉, 부서진 포탑이 존재할 수 있습니다. | ||
|
||
아래와 같은 4×4 크기의 격자를 생각해보겠습니다. (1,1), (2,2), (3,2) 를 포함한 총 7개의 칸은 이미 부서진 포탑을 의미합니다. | ||
|
||
<img src="https://contents.codetree.ai/problems/3506/images/01954636-dcbb-4032-91e4-f1aeccdfa92f.png" width="50%" height="50%"> | ||
|
||
하나의 턴은 다음의 4가지 액션을 순서대로 수행하며, 총 *K*번 반복됩니다. | ||
만약 부서지지 않은 포탑이 1개가 된다면 그 즉시 중지됩니다. | ||
|
||
### 1. 공격자 선정 | ||
|
||
부서지지 않은 포탑 중 **가장 약한 포탑**이 공격자로 선정됩니다. 공격자로 선정되면 가장 약한 포탑이므로, 핸디캡이 적용되어 ***N\*+\*M\*만큼의 공격력이 증가**됩니다. | ||
|
||
**가장 약한 포탑**은 다음의 기준으로 선정됩니다. | ||
|
||
1. **공격력이 가장 낮은** 포탑이 가장 약한 포탑입니다. | ||
2. 만약 공격력이 가장 낮은 포탑이 2개 이상이라면, **가장 최근에 공격한 포탑**이 가장 약한 포탑입니다. (모든 포탑은 시점 0에 모두 공격한 경험이 있다고 가정하겠습니다.) | ||
3. 만약 그러한 포탑이 2개 이상이라면, 각 포탑 위치의 **행과 열의 합이 가장 큰** 포탑이 가장 약한 포탑입니다. | ||
4. 만약 그러한 포탑이 2개 이상이라면, 각 포탑 위치의 **열 값이 가장 큰** 포탑이 가장 약한 포탑입니다. | ||
|
||
위의 예시에서 공격자를 선정해보면, 가장 낮은 공격력은 1이기 때문에, 아래 그림과 같이 (1,2) 위치에 있는 포탑이 공격자로 선정되며, 공격력이 8(=4(*N*)+4(*M*))만큼 증가하여 9가 됩니다. | ||
|
||
<img src="https://contents.codetree.ai/problems/3506/images/09739233-1fbe-4b78-b940-9f000a69ddfa.png" width="50%" height="50%"> | ||
|
||
### 2. 공격자의 공격 | ||
|
||
위에서 선정된 공격자는 자신을 제외한 **가장 강한 포탑**을 공격합니다. | ||
**가장 강한 포탑**은 위에서 정한 가장 약한 포탑 선정 기준의 반대이며, 다음과 같습니다. | ||
|
||
1. **공격력이 가장 높은** 포탑이 가장 강한 포탑입니다. | ||
2. 만약 공격력이 가장 높은 포탑이 2개 이상이라면, **공격한지 가장 오래된 포탑**이 가장 강한 포탑입니다. (모든 포탑은 시점 0에 모두 공격한 경험이 있다고 가정하겠습니다.) | ||
3. 만약 그러한 포탑이 2개 이상이라면, 각 포탑 위치의 **행과 열의 합이 가장 작은** 포탑이 가장 강한 포탑입니다. | ||
4. 만약 그러한 포탑이 2개 이상이라면, 각 포탑 위치의 **열 값이 가장 작은** 포탑이 가장 강한 포탑입니다. | ||
|
||
위의 예시에서 공격 대상자를 선정해보겠습니다. 공격 대상인 가장 강한 포탑은 공격력이 가장 큰 26의 공격력을 가진 (3,4) 위치의 포탑이 됩니다. | ||
|
||
<img src="https://contents.codetree.ai/problems/3506/images/ebed2fad-f1c1-4a94-ac8c-fb051363e70a.png" width="50%" height="50%"> | ||
|
||
공격을 할 때에는 **레이저 공격**을 먼저 시도하고, 만약 그게 안 된다면 **포탄 공격**을 합니다. 각 공격의 규칙은 다음과 같습니다. | ||
|
||
#### (1) 레이저 공격 | ||
|
||
레이저는 다음의 규칙으로 움직입니다. | ||
|
||
1. 상하좌우의 4개의 방향으로 움직일 수 있습니다. | ||
2. 부서진 포탑이 있는 위치는 지날 수 없습니다. | ||
3. 가장자리에서 막힌 방향으로 진행하고자 한다면, 반대편으로 나옵니다. (예를 들어, 위의 예시에서 (2,3)에서 오른쪽으로 두번 이동한다면, (2,3) -> (2,4) -> (2,1) 순으로 이동합니다.) | ||
|
||
레이저 공격은 공격자의 위치에서 공격 대상 포탑까지의 최단 경로로 공격합니다. 만약 그러한 경로가 존재하지 않는다면 (2) 포탄 공격을 진행합니다. 만약 경로의 길이가 똑같은 최단 경로가 2개 이상이라면, 우/하/좌/상의 우선순위대로 먼저 움직인 경로가 선택됩니다. | ||
|
||
최단 경로가 정해졌으면, 공격 대상에는 공격자의 공격력 만큼의 피해를 입히며, 피해를 입은 포탑은 해당 수치만큼 공격력이 줄어듭니다. 또한 공격 대상을 제외한 레이저 경로에 있는 포탑도 공격을 받게 되는데, 이 포탑은 공격자 공격력의 절반 만큼의 공격을 받습니다. (절반이라 함은 공격력을 2로 나눈 몫을 의미합니다.) | ||
|
||
#### (2) 포탄 공격 | ||
|
||
공격 대상에 포탄을 던집니다. 공격 대상은 공격자 공격력 만큼의 피해를 받습니다. 추가적으로 주위 8개의 방향에 있는 포탑도 피해를 입는데, 공격자 공격력의 절반 만큼의 피해를 받습니다. (절반이라 함은 공격력을 2로 나눈 몫을 의미합니다.) 공격자는 해당 공격에 영향을 받지 않습니다. 만약 가장자리에 포탄이 떨어졌다면, 위에서의 레이저 이동처럼 포탄의 추가 피해가 반대편 격자에 미치게 됩니다. | ||
|
||
위의 예시에서 공격자가 공격 대상을 레이저로 공격하기 위한 최단 경로는 아래 그림과 같습니다. 최단 경로가 2개 이상 있지만, **오른쪽**으로 먼저 움직이는 것이 우선 순위가 높기 때문에, 다음의 경로가 선택된 것입니다. 최단 경로가 정해졌기 때문에, 공격 대상에는 9만큼의 피해를 입히고, 경로에 있는 포탑에는 4(=9/2) 만큼의 피해를 입힙니다. | ||
|
||
<img src="https://contents.codetree.ai/problems/3506/images/5b8dca75-fffb-400f-ae97-c5c365aad305.png" width="50%" height="50%"> | ||
|
||
### 3. 포탑 부서짐 | ||
|
||
공격을 받아 공격력이 0 이하가 된 포탑은 부서집니다. | ||
|
||
### 4. 포탑 정비 | ||
|
||
공격이 끝났으면, 부서지지 않은 포탑 중 공격과 무관했던 포탑은 공격력이 1씩 올라갑니다. 공격과 무관하다는 뜻은 공격자도 아니고, 공격에 피해를 입은 포탑도 아니라는 뜻입니다. | ||
|
||
위의 예시에서 공격과 무관했던 다음 4개의 포탑은 정비를 받아 공격력이 1씩 증가합니다. 이렇게 첫 번째 턴이 종료됩니다. | ||
|
||
<img src="https://contents.codetree.ai/problems/3506/images/eb4fd7a6-356f-4478-b8d7-a418c016053c.png" width="50%" height="50%"> | ||
|
||
|
||
|
||
다음 두 번째 턴이 시작되면, 다시 공격자 선정이 시작됩니다. | ||
첫 번째 규칙에 따라 가장 공격력이 작은 9 만큼의 공격력을 가진 (1,2), (2,1), (2,4), (3,1) 위치의 포탑이 공격자 후보가 됩니다. 두 번째 규칙에 의해 가장 최근에 공격한 포탑인 (1,2) 포탑이 가장 약한 포탑이므로 공격자로 선정됩니다. 마찬가지로 핸디캡으로 8 만큼의 공격력을 얻습니다. | ||
|
||
<img src="https://contents.codetree.ai/problems/3506/images/60d600fc-f909-4dac-9c46-f2c6f8a68ca0.png" width="50%" height="50%"> | ||
|
||
다음으로 공격 대상을 선택하면, 공격력이 가장 높은 (3,4)가 선택됩니다. 공격자의 위치에서 공격 대상의 위치까지 이동하는 경로가 없기 때문에, 레이저 공격은 불가능하여 포탄 공격을 수행하게 됩니다. 포탄은 공격 대상인 (3,4) 위치에 떨어지며, 공격 대상은 17만큼의 피해를 받고, 주변 8개의 방향에 있는 포탑은 8만큼의 피해를 받습니다. | ||
|
||
<img src="https://contents.codetree.ai/problems/3506/images/89662d09-148e-48df-870c-ec4193618035.png" width="50%" height="50%"> | ||
|
||
마지막으로 공격과 무관했던 포탑은 정비를 받아 1 만큼의 공격력을 얻지만, 위의 예시에서는 부서지지 않은 포탑 중 공격과 무관했던 포탑이 없으므로, 최종 모습은 다음과 같습니다. | ||
|
||
<img src="https://contents.codetree.ai/problems/3506/images/65f7d6e8-f0e8-4930-b892-a3aedb046787.png" width="50%" height="50%"> | ||
|
||
전체 과정이 종료된 후 남아있는 포탑 중 가장 강한 포탑의 공격력을 출력하는 프로그램을 작성해보세요. | ||
|
||
## 풀이 방법 | ||
|
||
BFS 역추적 + 정렬 문제 | ||
|
||
|
||
|
||
**생각해봐야 하는 점** | ||
|
||
I. *레이저 공격 시* | ||
|
||
1. **우 하 좌 상** 순서로 탐색. 배열 공간을 벗어나면 반대편으로 나옴(지구 모양 공간) | ||
2. <span style="color: red">공격 대상</span>에게는 <span style="color: blue">공격자</span>의 **full 공격력** / 레이저 경로에 있는 포탑은 <span style="color: blue">공격자</span> 공격력의 **1/2** 피해를 받음 | ||
3. DFS 백트래킹을 사용한다면 시간 초과가 발생한다. | ||
4. BFS를 사용할 때는 역추적을 사용해야 한다. | ||
|
||
|
||
|
||
II. *포탄 공격 시* | ||
|
||
1) <span style="color: red">공격 대상</span> 및 주위 8개 지역에 피해를 입힘. 8개 지역이 배열 공간을 벗어나면 반대편에 피해를 입힘(지구 모양 공간) | ||
1) <span style="color: blue">공격자</span>는 주위 8개 지역에 포함되더라도 **공격을 입지 않음** | ||
|
||
|
||
|
||
<hr/> | ||
|
||
(a) getAttackAndDefense 메소드 | ||
|
||
1. <span style="color: blue">공격자</span> 및 <span style="color: red">공격 대상</span>을 선정하는 메소드 | ||
2. 배열 전체를 순회하며 공격력이 0 이상인 요소(부서지지 않은 포탑) 을 리스트에 삽입 | ||
3. 리스트 크기가 2 이하라면, 부서지지 않은 포탑이 하나 이하로 남은 것을 의미하기 때문에 null을 리턴 | ||
4. 리스트를 공격자 위주로 정렬 후, 0번째 요소를 <span style="color: blue">공격자</span>, 마지막 요소를 <span style="color: red">공격 대상</span>으로 선정 | ||
|
||
|
||
|
||
|
||
(b) razorAttack 메소드 | ||
|
||
1. <span style="color: blue">공격자</span>에서 시작해서 BFS로 탐색한다. 역추적을 위해서 2차원배열 tmpTur를 만듦 | ||
* tmpTur의 이전 위치(queue.poll)에 다음 위치(nr, nc)를 삽입 | ||
* <span style="color: red">공격 대상</span>에서 역으로 추적해 <span style="color: blue">공격자</span>를 찾아가는 원리 | ||
2. 만약 <span style="color: red">공격 대상</span>에 도달하지 못했다면 bombAttack 메소드로 진입 | ||
3. 지나간 경로에 피해를 입히면서, atkList에 담아 포탑 정비 섹션에서 사용 | ||
|
||
|
||
|
||
(c) bombAttack 메소드 | ||
|
||
1. <span style="color: red">공격 대상</span> 및 주위 8칸에 피해를 입히면서, atkList에 담아 포탑 정비 섹션에서 사용 | ||
* <span style="color: blue">공격자</span>는 **<span style="color: green">피해를 입지 않음</span>**에 유의!!! | ||
|
||
|
||
|
||
(d) maintenance 메소드 | ||
|
||
1. 2차원 배열 내 atkList에 없는 내용들 중, 부서지지 않은 포탑들만 1씩 체력을 올려줌 | ||
|
||
|
||
|
||
|
||
|
||
DFS 시간 초과, 정렬, 포탄 공격 시 공격자도 피해 입음 등 여러 가지 이슈를 겪으며 시간을 많이 소요했다. | ||
|
||
걸린 시간: 8시간 | ||
|
||
## 풀이 코드 | ||
|
||
<script src="https://gist.github.com/piacu/3268a803cb035b248f90e1a3b86d41ab.js"></script> |