1) 문제
수빈이는 강호와 함께 스타크래프트 게임을 하고 있다. 수빈이는 뮤탈리스크 1개가 남아있고, 강호는 SCV N개가 남아있다.
각각의 SCV는 남아있는 체력이 주어져있으며, 뮤탈리스크를 공격할 수는 없다. 즉, 이 게임은 수빈이가 이겼다는 것이다.
뮤탈리스크가 공격을 할 때, 한 번에 세 개의 SCV를 공격할 수 있다.
- 첫 번째로 공격받는 SCV는 체력 9를 잃는다.
- 두 번째로 공격받는 SCV는 체력 3을 잃는다.
- 세 번째로 공격받는 SCV는 체력 1을 잃는다.
SCV의 체력이 0 또는 그 이하가 되어버리면, SCV는 그 즉시 파괴된다. 한 번의 공격에서 같은 SCV를 여러 번 공격할 수는 없다.
남아있는 SCV의 체력이 주어졌을 때, 모든 SCV를 파괴하기 위해 공격해야 하는 횟수의 최솟값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 SCV의 수 N (1 ≤ N ≤ 3)이 주어진다. 둘째 줄에는 SCV N개의 체력이 주어진다. 체력은 60보다 작거나 같은 자연수이다.
출력
첫째 줄에 모든 SCV를 파괴하기 위한 공격 횟수의 최솟값을 출력한다.
예제 입력 1
3
12 10 4
예제 출력 1
ㅈ
2) 풀이
2.1) 입력조건
- 완전 탐색으로 풀수 있는가? :
- 최악의 조건으로 생각 했을 때 scv체력은 60,60,60
- 한마리는 계속 1씩 데미지를 받는다고 가정
- 시간복잡도는 6^10은 대략 6천만 >= 1천만
-> 중복된 요소를 줄이면서 탐색 필요
- dfs/bfs로 문제 풀이
2.2) 풀이과정(bfs)(시간초과)
- bfs를 통해 최소 depth를 구하고, 최소 depth를 계속해서 갱신
- 시간초과 : 가장 마지막의 최소depth가 나오면 시간이 6천만번+원복시간 복잡도 계산 필요
2.3) 코드
#include <bits/stdc++.h>
using namespace std;
int n, scv[3], a[] = {-9,-3,-1}, cnt, min_attack, sum;
vector<int> v;
vector<vector<int>> li;
void go(int depth, int count){
int flag = 0;
for (int hp : scv){
if (hp <= 0) {
flag++;
}
}
//전체가 0일때 최소 비교
if(flag == 3){
min_attack= min(min_attack,count);
return ;
}
if (depth == min_attack){
return;
}
for (int i = 0; i < li.size(); i++){
for (int j = 0; j < n; j++){
scv[j] += li[i][j];
}
cnt++;
go(depth+1,cnt);
for (int j = 0; j < n; j++){
scv[j] -= li[i][j];
}
cnt--;
}
}
int main(){
cin >> n;
for (int i = 0; i < n; i++){
cin >> scv[i];
sum += scv[i];
}
min_attack = sum / 13 + 1;
do {
for (int it : a){
v.push_back(it);
}
li.push_back(v);
v.clear();
} while (next_permutation(a, a + 3));
go(0,cnt);
cout << min_attack;
}
3) 해설
3.1) 조건
- scv의 체력을 (x,y,z)라고 생각하면 3차원 공간에서 (0,0,0)까지 가는 최단거리
3.2) 사용된 알고리즘
- int형을 return 값으로 가지는 dfs
//3차원 좌표설정
struct A{
int x,y,z;
};
queue<A> q;
q.push({x,y,z}); //큐를 만들어서 탐색 시작
visited[x][y][z] = 1; //시작점 방문
while(q.size()){
int a = q.front().a; //x좌표 대입
int b = q.front().b; //y좌표 대입
int c = q.front().c; //z좌표 대입
q.pop();
if(visited[0][0][0]) break; //최단거리로 도착했으므로 반복문 종료
for (vector<int> iv : list){ //list는 데미지 순열
int na = a + iv[0]; //다음 x좌표 대입
int nb = b + iv[1]; //다음 y좌표 대입
int nc = c + iv[2]; //다음 z좌표 대입
if(visited[na][nb][nc]) continue;
visited[na][nb][nc] = visited[a][b][c] + 1; //다음 방문 노드의 거리 추가
q.push({na,nb,nc}); //다음 노드 추가
}
}
3.3) 완성된 코드
- int형을 return 값으로 가지는 dfs
#include <bits/stdc++.h>
using namespace std;
int n, a[64][64][64], visited[64][64][64], hp[3];
int de[3] = {-9, -3, -1};
vector<int> v;
vector<vector<int>> d;
struct A
{
int a, b, c;
};
queue<A> q;
void make_list()
{
do
{
for (int i : de)
{
v.push_back(i);
}
d.push_back(v);
v.clear();
} while (next_permutation(de, de + n));
}
int bfs(int x, int y, int z)
{
visited[x][y][z] = 1;
q.push({x, y, z});
while (q.size())
{
int x = q.front().a;
int y = q.front().b;
int z = q.front().c;
q.pop();
if (visited[0][0][0])
break;
for (vector<int> iv : d)
{
int nx = max(0, x + iv[0]);
int ny = max(0, y + iv[1]);
int nz = max(0, z + iv[2]);
if (visited[nx][ny][nz])
continue;
visited[nx][ny][nz] = visited[x][y][z] + 1;
q.push({nx, ny, nz});
}
}
return visited[0][0][0] - 1;
}
int main()
{
cin >> n;
for (int i = 0; i < 3; i++)
{
cin >> hp[i];
}
make_list();
cout << bfs(hp[0], hp[1], hp[2]);
}
'개발자 꿈나무의 하루 > 02_Coding Test' 카테고리의 다른 글
(코딩테스트) 백준-13913번 숨박꼭질4(bfs+trace) (0) | 2024.06.12 |
---|---|
(코딩테스트) 백준-12851번 숨박꼭질2(bfs) (0) | 2024.06.12 |
(코딩테스트) 백준-16637번 괄호추가하기(재귀함수+누적합) (1) | 2024.06.10 |
(코딩테스트) 백준 17298번 - 오큰수(Stack) (0) | 2024.05.28 |
(코딩테스트) 백준 1325번 - 효율적인 해킹(dfs) (0) | 2024.05.27 |