개발자 꿈나무의 하루/02_Coding Test

(코딩테스트) 백준-12896번 뮤탈리스크(bfs)

kk_naks 2024. 6. 5. 10:43

 

1) 문제 

 
문제

수빈이는 강호와 함께 스타크래프트 게임을 하고 있다. 수빈이는 뮤탈리스크 1개가 남아있고, 강호는 SCV N개가 남아있다.

각각의 SCV는 남아있는 체력이 주어져있으며, 뮤탈리스크를 공격할 수는 없다. 즉, 이 게임은 수빈이가 이겼다는 것이다.

뮤탈리스크가 공격을 할 때, 한 번에 세 개의 SCV를 공격할 수 있다.

  1. 첫 번째로 공격받는 SCV는 체력 9를 잃는다.
  2. 두 번째로 공격받는 SCV는 체력 3을 잃는다.
  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]);
}