문제 링크


www.acmicpc.net/problem/17135

 

17135번: 캐슬 디펜스

첫째 줄에 격자판 행의 수 N, 열의 수 M, 궁수의 공격 거리 제한 D가 주어진다. 둘째 줄부터 N개의 줄에는 격자판의 상태가 주어진다. 0은 빈 칸, 1은 적이 있는 칸이다.

www.acmicpc.net

접근 방법


 설계만 잘한다면 어렵지 않게 풀 수 있는 문제였습니다.

 

1. 궁수가 3명 필요하므로 3명을 세울 수 있는 조합을 모두 찾는다.

2. 조합이 완성되었을 때, 궁수 각자의 표적을 찾는다.

3. 표적을 제거 후 다음 턴을 진행한다.  

 

1. 궁수가 3명 필요하므로 3명을 세울 수 있는 조합을 모두 찾는다.

void comb(int cnt, int start, vector<int> &archers) {
	if (cnt == 3) {
		int result = 0;
		copyTmp();
		for (int i = 0; i < n; i++) {
			result += thisTurn(archers);
		}
		ans = max(ans, result);
		return;
	}
	for (int i = start + 1; i < m; i++) { //m이 가로 길이
		archers.push_back(i);
		comb(cnt + 1, i, archers);
		archers.pop_back();
	}
}

backtracking을 사용하며 가능한 모든 궁수 3명의 조합을 찾을 수 있습니다.

조합이 완성되면 게임을 진행하며 총 몇명의 적(result)이 제거 되었는지 카운트 합니다.

thisTurn()은 한 턴에서 제거된 적을 계산하며, 한 게임은 세로길이는 n턴이 지나야 게임이 끝나므로 n번 반복합니다.

 

2. 조합이 완성되었을 때, 궁수 각자의 표적을 찾는다.

int thisTurn(vector<int> &archers) {

	int cnt = 0;

	vector <pair<int, int>> targets;
	for (int i = 0; i < archers.size(); i++) {
		pair<int, int> target = decideTarget(archers[i]);
		if (target.first != 999 && target.second != 999) {
			targets.push_back(target);
		}
	}
	for (int i = 0; i < targets.size(); i++) {
		pair<int, int> target = targets[i];
		if (tmp[target.first][target.second] == 1) {
			tmp[target.first][target.second] = 0;
			cnt++;
		}
	}

	for (int i = n - 2; i >= 0; i--)
		for (int j = 0; j < m; j++) 
			tmp[i + 1][j] = tmp[i][j];
	for (int j = 0; j < m; j++)
		tmp[0][j] = 0;

	return cnt;
}

궁수 한명의 표적을 찾는 일은 decideTarget()에서 진행됩니다. 제거할 모든 적을 vector<pair<int, int>> targets에 저장한 뒤 적을 제거하며 cnt를 카운트합니다. 이렇게 한턴이 종료 되면 적들을 한칸 씩 밑으로 이동 시킵니다.

 

3. pair<int, int> decideTarget(int archer)

pair<int, int> decideTarget(int archer) {
	
	bool visited[15][15] = { false };
	pair<int, int> nextdir[3] = { {-1,0}, {0,1}, {0,-1} };
	pair<int, int> ret = { 999, 999 };
	queue<coord> q;
	int dist = 999;

	q.push({ n - 1, archer, 1 });

	while (!q.empty()) {

		coord now = q.front();
		q.pop();

		if ((dist != 999 && dist < now.dist) || now.dist > d) //이미 표적을 잡았는데 더 멀리 갈때, 공격 범위를 벗어 낫을 때
			break;

		if (tmp[now.x][now.y] == 1) {
			if (dist >= now.dist) {
				dist = now.dist;
				if(ret.second > now.y) //기존 표적보다 더 왼쪽에 있을 때
					ret = { now.x, now.y };
			}
		}

		for (int j = 0; j < 3; j++) {
			int next_x = now.x + nextdir[j].first;
			int next_y = now.y + nextdir[j].second;
			if (0 <= next_x && next_x < n && 0 <= next_y && next_y < m) {
				if (!visited[next_x][next_y]) {
					visited[next_x][next_y] = true;
					q.push({ next_x, next_y, now.dist + 1 });
				}
			}
		}
	}
	return ret;
}

 궁수 한명이 표적을 찾는 함수입니다.  가장 짧은 거리의 적을 표적으로 삼는 조건이 있기 때문에 DFS보다 BFS가 적합하다고 판단하였습니다. 

소스 코드


#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

int map[15][15];
int tmp[15][15];
int n, m, d;
int ans = 0;

typedef struct coord {
	int x;
	int y;
	int dist;
};

void copyTmp() {
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			tmp[i][j] = map[i][j];
}

pair<int, int> decideTarget(int archer) {
	
	bool visited[15][15] = { false };
	pair<int, int> nextdir[3] = { {-1,0}, {0,1}, {0,-1} };
	pair<int, int> ret = { 999, 999 };
	queue<coord> q;
	int dist = 999;

	q.push({ n - 1, archer, 1 });

	while (!q.empty()) {

		coord now = q.front();
		q.pop();

		if ((dist != 999 && dist < now.dist) || now.dist > d) //이미 표적을 잡았는데 더 멀리 갈때, 공격 범위를 벗어 낫을 때
			break;

		if (tmp[now.x][now.y] == 1) {
			if (dist >= now.dist) {
				dist = now.dist;
				if(ret.second > now.y) //기존 표적보다 더 왼쪽에 있을 때
					ret = { now.x, now.y };
			}
		}

		for (int j = 0; j < 3; j++) {
			int next_x = now.x + nextdir[j].first;
			int next_y = now.y + nextdir[j].second;
			if (0 <= next_x && next_x < n && 0 <= next_y && next_y < m) {
				if (!visited[next_x][next_y]) {
					visited[next_x][next_y] = true;
					q.push({ next_x, next_y, now.dist + 1 });
				}
			}
		}
	}
	return ret;
}

int thisTurn(vector<int> &archers) {

	int cnt = 0;

	vector <pair<int, int>> targets;
	for (int i = 0; i < archers.size(); i++) {
		pair<int, int> target = decideTarget(archers[i]);
		if (target.first != 999 && target.second != 999) {
			targets.push_back(target);
		}
	}
	for (int i = 0; i < targets.size(); i++) {
		pair<int, int> target = targets[i];
		if (tmp[target.first][target.second] == 1) {
			tmp[target.first][target.second] = 0;
			cnt++;
		}
	}

	for (int i = n - 2; i >= 0; i--)
		for (int j = 0; j < m; j++) 
			tmp[i + 1][j] = tmp[i][j];
	for (int j = 0; j < m; j++)
		tmp[0][j] = 0;

	return cnt;
}

void comb(int cnt, int start, vector<int> &archers) {
	if (cnt == 3) {
		int result = 0;
		copyTmp();
		for (int i = 0; i < n; i++) {
			result += thisTurn(archers);
		}
		ans = max(ans, result);
		return;
	}
	for (int i = start + 1; i < m; i++) { //m이 가로 길이
		archers.push_back(i);
		comb(cnt + 1, i, archers);
		archers.pop_back();
	}
}

void solve() {
	vector<int> archers;
	comb(0, -1, archers);
}

int main() {

	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	cin >> n >> m >> d;

	for (int i = 0; i < n; i++) 
		for (int j = 0; j < m; j++) 
			cin >> map[i][j];
		
	solve();
	cout << ans;
}

개발 환경 : VS2019

질문, 지적 환영합니다!

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 9470 Strahler 순서  (0) 2020.09.25
[C++] 백준 17070 파이프 옮기기 1  (0) 2020.09.18
[C++] 백준 5670 휴대폰 자판  (0) 2020.09.10
[C++] 백준 10266 시계 사진들  (0) 2020.09.09
[C++] 백준 1300 k번째 수  (0) 2020.09.07

+ Recent posts