PS문제들

[백준] 20058: 마법사 상어와 파이어스톰(C++)

실실쟌 2022. 10. 1. 15:46

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

 

20058번: 마법사 상어와 파이어스톰

마법사 상어는 파이어볼과 토네이도를 조합해 파이어스톰을 시전할 수 있다. 오늘은 파이어스톰을 크기가 2N × 2N인 격자로 나누어진 얼음판에서 연습하려고 한다. 위치 (r, c)는 격자의 r행 c

www.acmicpc.net

해리포터 상어와 마법사의 돌

 

소요 시간

골드 2 기준 문제 해석부터 맞았습니다! 까지 52분이 소요되었습니다.

딱 골드 4정도 문제였던 것 같네요. 중간에 얼음이 녹는 부분이 동시에 일어나야 한다는 점을 빼먹는 바람에 디버깅에 시간이 좀 걸렸습니다. "동시에 일어난다" 같은 표현이 없어도 당연히 동시에 녹는 걸로 판단해야 하는데, 잘못했네요... 반성합니다.

시뮬레이션 문제는 골드 2~3은 1시간, 골드 4~5는 40분만에 "확실하게" 풀고 싶은데, 아직 내공이 부족한 것 같습니다.

 

구상

1. 파이어스톤 함수

- 전체를 2^L * 2^L 부분 격자로 나눕니다.

    - x, y각각 0부터 시작해서 2*L씩 더해 가면서 각 격자의 좌상단을 구합니다.

- 구해진 좌상단들을 통해 rotate함수를 호출하여 각 부분격자를 회전시킵니다.

- 얼음을 1 녹이는 함수 호출

- 합을 구합니다.

- 덩어리 크기를 계산하는 함수 호출

- 합과 크기 리턴

 

2. 시계방향으로 90도 rotate하는 함수

- 90도 rotate면 각 (x,y)를 (y,N-x-1)자리로 옮기면 됩니다.

- 이걸 하나의 배열 위에 진행하면 아직 변환되지 않은 값이 corrupt 됩니다.

- 따라서 임시배열을 만들어서 변환값을 저장하고 그 변환값을 다시 원 배열에 넣어 줍니다.

 

3. 전체 얼음에서 인접영역 얼음이 3개 미만인 얼음을 1만큼 녹이는 함수

- 항상 하던 대로 상하좌우 검사해서 인접영역의 얼음 개수를 구합니다.

- 얼음 개수가 3보다 작은 칸들은 따로 표시를 해두고 마지막에 한번에 -1 해줍니다.

 

4. 가장 큰 덩어리의 크기를 계산하는 함수

- 기본적으로 bfs를 해야 합니다. (이런 종류의 구역 나누기 문제는 주로 bfs 사용합니다. 방문만 하면 되기 때문)

- 모든 얼음을 방문할 때까지 얼음이 있으면서 방문하지 않은 칸마다 bfs로 연결된 얼음을 방문합니다.

 

5. 얼음의 합을 구하는 것은 귀찮아서 함수를 만들지 않았습니다.

- 루프문 두개 돌려서 할 수 있습니다.

 

코드

#include <string>
#include <vector>
#include<iostream>
#include <climits>
#include<queue>
#include<cmath>
#include<algorithm>

using namespace std;

int dx[4] = {0,0,-1,1};
int dy[4] = {-1,1,0,0};

void do_rotate(int x, int y, int len, vector<vector<int> >& ices) {
    vector<vector<int> > temp; 
    temp.resize(len);
    for(int i=0; i<len; i++) {
        temp[i].resize(len);
    }
    for(int i=x; i<x+len; i++) {
        for(int j=y; j<y+len; j++) {
            temp[j-y][len-(i-x)-1] = ices[i][j];
        }
    }
    for(int i=x; i<x+len; i++) {
        for(int j=y; j<y+len; j++) {
            ices[i][j] = temp[i-x][j-y];
        }
    }
}

void remove_ice(int N, vector<vector<int> >& ices) {
    int total = pow(2, N);
    vector<vector<int> > add;
    add.resize(total);
    for(int i=0; i<total; i++) {
        add[i].resize(total);
    }
    for(int i=0; i<total; i++) {
        for(int j=0; j<total; j++) {
            if(ices[i][j] == 0) continue;
            int numOfIce = 0;
            for(int k=0; k<4; k++) {
                int nx = i + dx[k]; int ny = j + dy[k];
                if(nx < 0 || nx > total-1 || ny < 0 || ny > total - 1 || ices[nx][ny] == 0) continue;
                else numOfIce ++;
            }
            if(numOfIce < 3)
                add[i][j] -= 1;
        }
    }

    for(int i=0; i<total; i++) {
        for(int j=0; j<total; j++) {
            ices[i][j] += add[i][j];
        }
    }
}

void do_firestrom(int N, int L, vector<vector<int> >& ices) {
    int len = pow(2, L);
    int total = pow(2, N);
    for(int i=0; i<total; i+= len) {
        for(int j=0; j<total; j+= len) {
            do_rotate(i, j, len, ices);
        }
    }
    remove_ice(N, ices);
}

int find_largest(int N, vector<vector<int> >& ices) {
    int largest = 0;
    int total = pow(2, N);
    bool visited[total][total];
    for(int i=0; i<total; i++) {
        fill_n(visited[i], total, false);
    }

    for(int i=0; i<total; i++) {
        for(int j=0; j<total; j++) {
            if(ices[i][j] != 0 && !visited[i][j]) {
                int ice_size = 0;
                queue<int> q; q.push(i*total + j);
                visited[i][j] = true;
                while(!q.empty()) {
                    int x = q.front()/total; int y = q.front()%total;
                    ice_size ++;
                    q.pop();
                    for(int k=0; k<4; k++) {
                        int nx = x + dx[k]; int ny = y + dy[k];
                        if(nx < 0 || nx > total-1 || ny < 0 || ny > total-1 || visited[nx][ny] || ices[nx][ny] == 0) continue;
                        visited[nx][ny] = true;
                        q.push(nx*total + ny);
                    }
                }
                largest = max(ice_size, largest);
            }
        }
    }

    return largest;
}

vector<int> solution(int N,int Q,vector<vector<int> >& ices, vector<int>& commands) {
    vector<int> answer; answer.resize(2);
    int total = pow(2, N);
    for(int i=0; i<Q; i++) {
        int L = commands[i];
        do_firestrom(N, L, ices);
    }

    for(int i=0; i<total; i++) {
        for(int j = 0; j<total; j++) {
            answer[0] += ices[i][j];
        }
    }
    answer[1] = find_largest(N, ices);

    return answer;
}

int main(int argc, char** argv)
{
    ios_base :: sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);
    int N, Q;
    cin >> N >> Q;
    int total = pow(2, N);

    vector<vector<int> > ices;
    ices.resize(total);
    for(int i=0; i<total; i++) {
        ices[i].resize(total);
        for(int j=0; j<total; j++) {
            cin >> ices[i][j];
        }
    }
    vector<int> commands;
    commands.resize(Q);
    for(int i=0; i<Q; i++) {
        cin >> commands[i];
    }
    
    vector<int> result = solution(N, Q, ices, commands);
    cout << result[0] << "\n" << result[1] << "\n";

    return 0;
}

 

댓글수0