관리 메뉴

공부 기록

백준 23290 마법사 상어와 복제 본문

코딩테스트/[BOJ] 문제 풀이

백준 23290 마법사 상어와 복제

_JAEJAE_ 2022. 3. 22. 00:09

https://www.acmicpc.net/problem/23290

 

23290번: 마법사 상어와 복제

첫째 줄에 물고기의 수 M, 상어가 마법을 연습한 횟수 S가 주어진다. 둘째 줄부터 M개의 줄에는 물고기의 정보 fx, fy, d가 주어진다. (fx, fy)는 물고기의 위치를 의미하고, d는 방향을 의미한다. 방향

www.acmicpc.net

문제

마법사 상어는 파이어볼토네이도, 파이어스톰, 물복사버그, 비바라기, 블리자드 마법을 할 수 있다. 오늘은 기존에 배운 물복사버그 마법의 상위 마법인 복제를 배웠고, 4 × 4 크기의 격자에서 연습하려고 한다. (r, c)는 격자의 r행 c열을 의미한다. 격자의 가장 왼쪽 윗 칸은 (1, 1)이고, 가장 오른쪽 아랫 칸은 (4, 4)이다.

격자에는 물고기 M마리가 있다. 각 물고기는 격자의 칸 하나에 들어가 있으며, 이동 방향을 가지고 있다. 이동 방향은 8가지 방향(상하좌우, 대각선) 중 하나이다. 마법사 상어도 연습을 위해 격자에 들어가있다. 상어도 격자의 한 칸에 들어가있다. 둘 이상의 물고기가 같은 칸에 있을 수도 있으며, 마법사 상어와 물고기가 같은 칸에 있을 수도 있다.

상어의 마법 연습 한 번은 다음과 같은 작업이 순차적으로 이루어진다.

  1. 상어가 모든 물고기에게 복제 마법을 시전한다. 복제 마법은 시간이 조금 걸리기 때문에, 아래 5번에서 물고기가 복제되어 칸에 나타난다.
  2. 모든 물고기가 한 칸 이동한다. 상어가 있는 칸, 물고기의 냄새가 있는 칸, 격자의 범위를 벗어나는 칸으로는 이동할 수 없다. 각 물고기는 자신이 가지고 있는 이동 방향이 이동할 수 있는 칸을 향할 때까지 방향을 45도 반시계 회전시킨다. 만약, 이동할 수 있는 칸이 없으면 이동을 하지 않는다. 그 외의 경우에는 그 칸으로 이동을 한다. 물고기의 냄새는 아래 3에서 설명한다.
  3. 상어가 연속해서 3칸 이동한다. 상어는 현재 칸에서 상하좌우로 인접한 칸으로 이동할 수 있다. 연속해서 이동하는 칸 중에 격자의 범위를 벗어나는 칸이 있으면, 그 방법은 불가능한 이동 방법이다. 연속해서 이동하는 중에 상어가 물고기가 있는 같은 칸으로 이동하게 된다면, 그 칸에 있는 모든 물고기는 격자에서 제외되며, 제외되는 모든 물고기는 물고기 냄새를 남긴다. 가능한 이동 방법 중에서 제외되는 물고기의 수가 가장 많은 방법으로 이동하며, 그러한 방법이 여러가지인 경우 사전 순으로 가장 앞서는 방법을 이용한다. 사전 순에 대한 문제의 하단 노트에 있다.
  4. 두 번 전 연습에서 생긴 물고기의 냄새가 격자에서 사라진다.
  5. 1에서 사용한 복제 마법이 완료된다. 모든 복제된 물고기는 1에서의 위치와 방향을 그대로 갖게 된다.

격자에 있는 물고기의 위치, 방향 정보와 상어의 위치, 그리고 연습 횟수 S가 주어진다. S번 연습을 모두 마쳤을때, 격자에 있는 물고기의 수를 구해보자.

입력

첫째 줄에 물고기의 수 M, 상어가 마법을 연습한 횟수 S가 주어진다. 둘째 줄부터 M개의 줄에는 물고기의 정보 fx, fy, d가 주어진다. (fx, fy)는 물고기의 위치를 의미하고, d는 방향을 의미한다. 방향은 8 이하의 자연수로 표현하고, 1부터 순서대로 ←, ↖, ↑, ↗, →, ↘, ↓, ↙ 이다. 마지막 줄에는 sx, sy가 주어지며, 상어가 (sx, sy)에 있음을 의미한다.

격자 위에 있는 물고기의 수가 항상 1,000,000 이하인 입력만 주어진다.

출력

S번의 연습을 마친 후 격자에 있는 물고기의 수를 출력한다.

제한

  • 1 ≤ M ≤ 10
  • 1 ≤ S ≤ 100
  • 1 ≤ fx, fy, sx, sy ≤ 4
  • 1 ≤ d ≤ 8
  • 두 물고기 또는 물고기와 상어가 같은 칸에 있을 수도 있다.

예제 입력 1 복사

5 1
4 3 5
1 3 5
2 4 2
2 1 6
3 4 4
4 2

예제 출력 1 복사

9

격자의 초기 상태는 다음 그림과 같다. 상어가 있는 칸은 배경색이 어두운 칸, 물고기는 방향으로 표시했다.

물고기가 한 칸 이동하면 다음과 같다.

상어는 [상, 상, 상]으로 이동한다. 이때 (3, 2)에 있는 물고기가 격자에서 제외된다. 물고기의 냄새가 있는 칸은 배경의 색이 빨간색이다.

이제 복제 마법이 완료된다.


[풀이 과정]

1. 문제에서는 (1, 1)부터 시작이지만 (0, 0)으로 변환시켰다. 방향도 배열의 0번 인덱스부터 시작하기 위해 -1씩 해줬다.

 

2. 상어가 마법의 연습한 횟수인 S만큼 아래 함수를 반복한다.

copy_magic() : 복제할 물고기를 fish 벡터에 저장하는 함수
move_fish() : 물고기 이동 함수
move_shark(0, 0, sx, sy) : 상어 이동 함수
update_arr(i) : 상어가 이동을 마치고 난 위치, 격자에 있는 물고기의 정보, 제외된 물고기, 냄새를 업데이트하는 함수
remove_smell(i) : 조건에 맞는 냄새를 지우는 함수
duplicate_fish() : copy_magic()에서 복제해놓은 물고기를 cur_fish벡터와 arr벡터에 추가하는 함수

 

 

# 실수: 방향을 문제에 주어진 것과 다르게 구현함, 상어의 움직임 전에 max_out_fish와 remove_fish를 초기화 까먹음


[소스 코드]

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAX = 5;

int M, S, sx, sy;
int max_out_fish = -1, max_sx, max_sy, smell[MAX][MAX];
vector <int> arr[MAX][MAX]; // 격자에 물고기를 표시하기 위한 벡터, 같은 위치에 여러 마리 가능함
struct FISH{
    int x, y, d;
    bool dead;
};
vector <FISH> fish; // 복제할 물고기를 저장하는 벡터
vector <FISH> cur_fish; // 현재 모든 물고기의 정보를 저장하는 벡터
vector <int> remove_fish; // 상어와 만나 제외된 물고기의 인덱스를 저장하는 벡터
vector <int> final_remove_fish; // 상어의 이동경로가 결정되었을 때 최종적으로 제외될 물고기의 인덱스를 저장하는 벡터

int f_dx[] = {0, -1, -1, -1, 0, 1, 1, 1}; // 물고기의 이동방향
int f_dy[] = {-1, -1, 0, 1, 1, 1, 0, -1};
int s_dx[] = {-1, 0, 1, 0}; // 상어의 이동방향
int s_dy[] = {0, -1, 0, 1};

void copy_magic(){
    fish.clear();
    for(int i=0;i<cur_fish.size();i++){
        if(!cur_fish[i].dead) {
            fish.push_back(cur_fish[i]);
        }
    }
}

bool check_range(int x, int y){
    if(x < 0 || x >= 4 || y < 0 || y >= 4) return false;
    else return true;
}

void move_fish(){
    memset(arr, 0, sizeof(arr));
    for (int i=0;i<cur_fish.size();i++){
        if(cur_fish[i].dead) continue;
        int x = cur_fish[i].x;
        int y = cur_fish[i].y;
        int d = cur_fish[i].d;
        int nx, ny; bool can_move = false;

        for(int j=0;j<8;j++) {
            nx = x + f_dx[d];
            ny = y + f_dy[d];
            if (!(nx == sx && ny == sy) && smell[nx][ny] == 0 && check_range(nx, ny)) {
                can_move = true;
                break;
            }
            d = (d + 7) % 8;
        }
        if (can_move) {
            cur_fish[i].x = nx, cur_fish[i].y = ny, cur_fish[i].d = d;
            arr[nx][ny].push_back(i);
        }
        else{
            cur_fish[i].x = x, cur_fish[i].y = y, cur_fish[i].d = d;
            arr[x][y].push_back(i);
        }
    }
}

void move_shark(int idx, int out_fish, int x, int y){
    if(idx >= 3){
        if(max_out_fish < out_fish){
            max_out_fish = out_fish;
            max_sx = x, max_sy = y;
            final_remove_fish = remove_fish;
        }
    }
    else{
        for(int i=0;i<4;i++){
            int nx = x + s_dx[i];
            int ny = y + s_dy[i];

            if(!check_range(nx, ny)) continue;
            vector <int> tmp = arr[nx][ny];

            for(int j=0;j<arr[nx][ny].size();j++) remove_fish.push_back(arr[nx][ny][j]);
            arr[nx][ny].clear();
            move_shark(idx+1, out_fish + tmp.size(), nx, ny);

            arr[nx][ny] = tmp;
            for(int j=0;j<arr[nx][ny].size();j++) remove_fish.pop_back();
        }
    }
}

void update_arr(int time){
    sx = max_sx, sy = max_sy;
    for(int i=0;i<final_remove_fish.size();i++){
        int x = cur_fish[final_remove_fish[i]].x;
        int y = cur_fish[final_remove_fish[i]].y;
        cur_fish[final_remove_fish[i]].dead = true;
        arr[x][y].clear();
        smell[x][y] = time;
    }
}

void remove_smell(int time){
    for(int i=0;i<4;i++){
        for(int j=0;j<4;j++){
            if(smell[i][j] > 0 && smell[i][j] == time - 2) smell[i][j] = 0;
        }
    }
}

void duplicate_fish(){
    int total_fish = cur_fish.size();

    for(int i=0;i<fish.size();i++){
        if(fish[i].dead) continue;
        cur_fish.push_back({fish[i].x, fish[i].y, fish[i].d, fish[i].dead});
        arr[fish[i].x][fish[i].y].push_back(total_fish);
        total_fish++;
    }
}



int main(){
    scanf("%d %d", &M, &S);

    for(int i=0;i<M;i++){
        int fx, fy, fd;
        scanf("%d %d %d", &fx, &fy, &fd);

        cur_fish.push_back({fx-1, fy-1, fd-1, false});
        arr[fx-1][fy-1].push_back(i);
    }

    scanf("%d %d", &sx, &sy);
    sx--; sy--;

    for(int i=1;i<=S;i++){
        copy_magic();
        move_fish();
        remove_fish.clear();
        max_out_fish = -1;
        move_shark(0, 0, sx, sy);
        update_arr(i);
        remove_smell(i);
        duplicate_fish();
    }

    int result = 0;
    for(int i=0;i<cur_fish.size();i++){
        if(!cur_fish[i].dead) result++;
    }
    printf("%d\n", result);
    return 0;
}

'코딩테스트 > [BOJ] 문제 풀이' 카테고리의 다른 글

백준 1713 후보 추천하기  (0) 2022.03.31
백준 12100 2048(Easy)  (0) 2022.03.25
백준 23289 온풍기 안녕!  (0) 2022.03.17
백준 23288 주사위 굴리기2  (0) 2022.03.14
백준 17822 원판 돌리기  (0) 2021.10.01
Comments