boj17135-풀이
문제설명
(n, m)의 격자판 위에 적이 존재하고,
n + 1번째 줄에는 궁수가 3명이 존재한다.
궁수는 각자 한자리씩 차지하며, 겹칠 수 없다.
모든 궁수는 공통된 사거리 d를 가진다.
궁수는 거리가 닿는 적들을 공격할 수 있다.
거리 계산은 궁수와 적의 (x축 거리 + y축 거리)로 한다.
공격 우선순위는 가장 가까운 적 > 가장 왼쪽에 있는 적 순서다.
궁수가 같은 타겟을 공격할 수 있다.
게임은 궁수가 공격 후 적이 한칸 아래로 내려오도록 진행한다.
격자를 나간 적은 공격할 수 없다.
위의 조건에 따라서, 궁수를 효율적으로 배치해 최대로 죽일 수 있는 적의 수를 구하는 문제
풀이
탐색순서를 나타내보면 위와 같다.
각 단계를 다른 색으로 구분했는데, 이를 보면 물이 퍼지는 듯한 형태를 띈다.
퍼지는 모양을 보고 bfs탐색을 떠올렸다.
또한 이를 통해 시뮬레이션 문제라고 생각하고 접근했다.
각 턴이 지날 때, 격자의 많은 수의 적을 매번 옮겨주는 것보다는 궁수가 올라가는 방향으로 진행했다. (하나하나 옮기면 비용이 많이 들어서)
다른 궁수끼리 같은 적을 선택할 수 있기 때문에, 타겟을 지정하는 즉시 공격이 아닌, 타겟 지정 후, 턴이 끝나기 전에 공격하는 방법으로 구현했다.
궁수 위치 지정 -> m의 턴동안, 궁수 별 타겟 지정 -> 적 공격 및 사살한 적 카운트 -> 턴 증가 -> 모든 턴 종료 후 최대 사살 수 업데이트와 같은 흐름으로 구현했다.
궁수 위치 지정은 next_permutation을 통한 조합생성으로 했고,
궁수 별 타겟 지정은 bfs를 통해서 턴을 궁수의 y좌표, 조합에서 구해진 위치를 궁수의 x좌표로 해서 타겟을 지정했다.
구현
#include <iostream>
#include <vector>
#include <queue>
#include <utility>
#include <algorithm>
using namespace std;
typedef pair<int,int> pii;
typedef vector<bool> vb;
typedef vector<vb> vvb;
#define y first
#define x second
int n, m, d;
vvb map(15, vb(15));
vvb isAlive;
pii operator+(pii p1, pii p2){
return {p1.first + p2.first, p1.second + p2.second};
}
int dist(pii d1, pii d2){
return (d1.x - d2.x) * (d1.x > d2.x ? 1 : -1) + (d1.y - d2.y) * (d1.y > d2.y ? 1 : -1);
}
bool isInArea(pii d){
return (0 <= d.x && d.x < m) && (0 <= d.y && d.y < n);
}
// n번째 턴 start에 위치한 궁수의 타겟 좌표 반환
pii bfs(int turn, int start){
vvb visited(n, vb(m, false));
pii origin = {n - turn, start};
queue<pii> q;
q.push(origin);
while(!q.empty()){
pii cur = q.front();
q.pop();
pii ways[3] = {
{0,-1},
{-1, 0},
{0, 1}
};
for (int i = 0; i < 3; ++i) {
pii next = cur + ways[i];
if(isInArea(next) && !visited[next.y][next.x] && origin.y > next.y){
if(dist(next, origin) <= d){
if(isAlive[next.y][next.x])
return next;
q.push(next);
}
visited[next.y][next.x] = true;
}
}
}
return {-1, -1};
}
int main()
{
cin >> n >> m >> d;
for(int i = 0; i < n; ++i){
for(int j = 0; j < m; ++j){
bool b;
cin >> b;
map[i][j] = b;
}
}
vector<int> p(m);
for(int i = 3; i < m; ++i) {p[i] = 0;}
p[0] = p[1] = p[2] = 1;
int maxKilled = 0;
do {
int killed = 0;
isAlive = map;
// 궁수 자리 배치
int archors[3];
int idx = 0;
for(int i = 0; i < m; ++i){
if(p[i])
archors[idx++] = i;
}
// 게임 진행(타겟 지정 -> 타겟 삭제 및 카운트 -> 턴 진행)
pii atk[3];
for(int t = 0; t < n; ++t){
// 타겟 지정
for(int a = 0; a < 3; ++a){
atk[a] = bfs(t, archors[a]);
}
// 타겟 삭제, 카운트
for(int a = 0; a < 3; ++a){
if(atk[a].y != -1
&& isAlive[atk[a].y][atk[a].x]){
isAlive[atk[a].y][atk[a].x] = false;
++killed;
}
}
// 턴 진행 (for문 내 ++t)
}
maxKilled = max(killed, maxKilled);
} while (prev_permutation(p.begin(), p.end()));
cout << maxKilled << endl;
return 0;
}
댓글남기기