코테 노트/백준

백준 20057 마법사 상어와 토네이도 C++

화요밍 2021. 9. 16. 17:39
728x90
반응형

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

 

20057번: 마법사 상어와 토네이도

마법사 상어가 토네이도를 배웠고, 오늘은 토네이도를 크기가 N×N인 격자로 나누어진 모래밭에서 연습하려고 한다. 위치 (r, c)는 격자의 r행 c열을 의미하고, A[r][c]는 (r, c)에 있는 모래의 양을

www.acmicpc.net

 

최종 코드

 

GitHub - hwayeon351/BEAKJOON-Algorithms: 백준 알고리즘 소스 코드 모음

백준 알고리즘 소스 코드 모음. Contribute to hwayeon351/BEAKJOON-Algorithms development by creating an account on GitHub.

github.com

//
//  main.cpp
//  BJ20057_1
//
//  Created by Hwayeon on 2021/10/18.
//

#include <iostream>
using namespace std;

int N;
int A[500][500] = {0, };

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

int spread[4][10][3] = {
    //0-좌
    {{0,-2,2}, {-1,-1,10}, {0,-1,7}, {1,-1,1}, {-2,0,5}, {-1,1,10}, {0,1,7}, {1,1,1}, {0,2,2}, {-1, 0, 0}},
    //1-하
    {{-1,-1,1}, {1,-1,1}, {-2,0,2}, {-1,0,7}, {1,0,7}, {2,0,2}, {-1,1,10}, {1,1,10}, {0,2,5}, {0,1,0}},
    //2-우
    {{0,-2,2}, {-1,-1,1}, {0,-1,7}, {1,-1,10}, {2,0,5}, {-1,1,1}, {0,1,7}, {1,1,10}, {0,2,2}, {1,0,0}},
    //3-상
    {{0,-2,5}, {-1,-1,10}, {1,-1,10}, {-2,0,2}, {-1,0,7}, {1,0,7}, {2,0,2}, {-1,1,1}, {1,1,1}, {0,-1,0}}
};

int tx, ty;
int gone = 0;

void spread_sand(int tx, int ty, int d){
    int ay = A[ty][tx];
    int total_spread = 0;
    A[ty][tx] = 0;
    int x, y, p, sand;
    for(int i=0; i<9; i++){
        x = tx + spread[d][i][0];
        y = ty + spread[d][i][1];
        p = spread[d][i][2];
        sand = ay*(0.01*p);
        if(x<0 || x>=N || y<0 || y>=N) gone += sand;
        else A[y][x] += sand;
        total_spread += sand;
    }
    x = tx + spread[d][9][0];
    y = ty + spread[d][9][1];
    if(x<0 || x>=N || y<0 || y>=N) gone += (ay-total_spread);
    else A[y][x] += (ay-total_spread);
}

void move_tornado(){
    int d = 0;
    for(int dis=1; dis<=N; dis++){
        int ndis = dis;
        for(int i=0; i<2; i++){
            if(dis == N) ndis = N-1;
            for(int j=0; j<ndis; j++){
                tx += dx[d];
                ty += dy[d];
                spread_sand(tx, ty, d);
            }
            if(dis == N) return;
            d = (d+1)%4;
        }
    }
}

int main(int argc, const char * argv[]) {
    cin >> N;
    for(int y=0; y<N; y++){
        for(int x=0; x<N; x++){
            cin >> A[y][x];
        }
    }
    tx = N/2;
    ty = N/2;
    move_tornado();
    cout << gone << endl;
    
    return 0;
}

풀이 과정

풀이 시간 1시간 11분

 

//
//  main.cpp
//  BJ20057_1
//
//  Created by Hwayeon on 2021/10/18.
//

#include <iostream>
using namespace std;

int N;

//A[r][c] = (r, c)에 있는 모래의 양
int A[500][500] = {0, };

//0-좌, 1-하, 2-우, 3-상
//토네이도 이동 방향이 좌->하->우->상으로 반복된다
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};

//spread[d][i] = {xx, yy, p}
//d 방향으로 토네이도가 이동하여 흩어진 모래가 이동할 칸과 비율 정보
//토네이도 = (tx, ty)일 때, (tx+xx, ty+yy)칸에 p%의 모래가 도착한다
int spread[4][10][3] = {
    //0-좌
    {{0,-2,2}, {-1,-1,10}, {0,-1,7}, {1,-1,1}, {-2,0,5}, {-1,1,10}, {0,1,7}, {1,1,1}, {0,2,2}, {-1, 0, 0}},
    //1-하
    {{-1,-1,1}, {1,-1,1}, {-2,0,2}, {-1,0,7}, {1,0,7}, {2,0,2}, {-1,1,10}, {1,1,10}, {0,2,5}, {0,1,0}},
    //2-우
    {{0,-2,2}, {-1,-1,1}, {0,-1,7}, {1,-1,10}, {2,0,5}, {-1,1,1}, {0,1,7}, {1,1,10}, {0,2,2}, {1,0,0}},
    //3-상
    {{0,-2,5}, {-1,-1,10}, {1,-1,10}, {-2,0,2}, {-1,0,7}, {1,0,7}, {2,0,2}, {-1,1,1}, {1,1,1}, {0,-1,0}}
};

//토네이도의 현재 위치
int tx, ty;
//격자 밖으로 나간 모래의 양
int gone = 0;

void spread_sand(int tx, int ty, int d){
	//y 칸의 모래의 양
    int ay = A[ty][tx];
    //흩날려진 모래의 양
    int total_spread = 0;
    
    //토네이도가 y칸으로 이동했으므로 y칸의 모래는 0으로 바꿔준다
    A[ty][tx] = 0;
    
    //비율이 적힌 9개의 칸에 모래를 흩날린다
    int x, y, p, sand;
    for(int i=0; i<9; i++){
  		//모래가 도착하는 칸
        x = tx + spread[d][i][0];
        y = ty + spread[d][i][1];
        
        //모래의 비율과 양
        p = spread[d][i][2];
        sand = ay*(0.01*p);
        
        //모래가 격자 밖으로 흩날리는 경우, gone에 모래의 양을 더한다
        if(x<0 || x>=N || y<0 || y>=N) gone += sand;
        //모래가 도착하는 칸에 흩날린 모래 양을 더한다
        else A[y][x] += sand;
        
        //흩날려진 모래의 양을 카운팅
        total_spread += sand;
    }
    
    //알파 칸의 위치
    x = tx + spread[d][9][0];
    y = ty + spread[d][9][1];
    //격자를 벗어나는 경우, gone에 모래의 양을 더한다
    if(x<0 || x>=N || y<0 || y>=N) gone += (ay-total_spread);
    //알파 칸에 나머지 모래를 더한다
    else A[y][x] += (ay-total_spread);
}

void move_tornado(){
	//처음 토네이도의 이동 방향은 0-좌
    int d = 0;
    
    //dis = d방향으로 이동하는 거리
    //1,1,2,2,3,3,4,4,...,N-1,N-1,N-1의 규칙을 가지고 이동거리가 변한다
    for(int dis=1; dis<=N; dis++){
        int ndis = dis;
        //dis에 대해 이동방향을 바꾸어 2번 이동한다
        for(int i=0; i<2; i++){
        	//마지막 맨 윗행의 경우만 예외적으로 N-1 이동
            if(dis == N) ndis = N-1;
            for(int j=0; j<ndis; j++){
            	//토네이도 d방향으로 한 칸 이동
                tx += dx[d];
                ty += dy[d];
                
                //모래 흩날리기 -> O(1)
                spread_sand(tx, ty, d);
            }
            //마지막 칸으로 이동한 경우, 함수 종료
            if(dis == N) return;
            
            //방향 바꾸기
            d = (d+1)%4;
        }
    }
}

int main(int argc, const char * argv[]) {
    cin >> N;
    for(int y=0; y<N; y++){
        for(int x=0; x<N; x++){
            cin >> A[y][x];
        }
    }
    //토네이도 처음 위치 초기화
    tx = N/2;
    ty = N/2;
    
    //토네이도 이동 시작 -> O(N^2)
    move_tornado();
    cout << gone << endl;
    
    return 0;
}
//시간복잡도 = O(N^2), 공간복잡도 = O(N^2)

이전 풀이

풀이 시간 2시간 46분

모래를 흩뿌리는 부분에서 4방향의 경우를 모두 나누어 각 칸에 흩뿌리도록 노가다로 구현했다...

각 경우에 따라 잘 흩뿌려지고 있는지를 확인하는 디버깅 과정에서 시간을 진짜 많이 소요했다...

그래도 3시간 안에 풀 수 있어서 다행이지만, 다음에는 흩뿌리는 과정을 간단한 로직으로 짜보도록 해야겠다.

#include <iostream>
using namespace std;

int N;
int board[500][500] = {0, };

//토네이도의 이동은 좌->하->우->상 방향으로 바뀌기 때문에 순서를 고려한다
int dc[4] = {-1, 0, 1, 0};
int dr[4] = {0, 1, 0, -1};

//격자 밖으로 나간 모래의 양을 담는 변수
long long total_out = 0;

void spread_sand(int r, int c, int d){
    int sum = 0;
    int per = 0;
    int out = 0;
    int y = board[r][c];
    switch (d) {
        //왼쪽
        case 0:
            per = y*0.02;
            if(r-2 >= 0){
                board[r-2][c] += per;
                sum += per;
            }
            else out += per;
            if(r+2 < N) {
                board[r+2][c] += per;
                sum += per;
            }
            else out += per;
            per = y*0.07;
            if(r-1 >= 0){
                board[r-1][c] += per;
                sum += per;
            }
            else out += per;
            if(r+1 < N){
                board[r+1][c] += per;
                sum += per;
            }
            else out += per;
            per = y*0.01;
            if(r-1 >= 0 && c+1 < N){
                board[r-1][c+1] += per;
                sum += per;
            }
            else out += per;
            if(r+1 < N && c+1 < N){
                board[r+1][c+1] += per;
                sum += per;
            }
            else out += per;
            per = y*0.1;
            if(r-1 >= 0 && c-1 >= 0){
                board[r-1][c-1] += per;
                sum += per;
            }
            else out += per;
            if(r+1 < N && c-1 >= 0){
                per = y*0.1;
                board[r+1][c-1] += per;
                sum += per;
            }
            else out += per;
            per = y*0.05;
            if(c-2 >= 0){
                board[r][c-2] += per;
                sum += per;
            }
            else out += per;
            if(c-1 >= 0) board[r][c-1] += (board[r][c] - (sum + out));
            else out += (board[r][c] - (sum + out));
            board[r][c] = 0;
            total_out += out;
            break;
        //아래쪽
        case 1:
            per = y*0.02;
            if(c-2 >= 0){
                board[r][c-2] += per;
                sum += per;
            }
            else out += per;
            if(c+2 < N) {
                board[r][c+2] += per;
                sum += per;
            }
            else out += per;
            per = y*0.07;
            if(c-1 >= 0){
                board[r][c-1] += per;
                sum += per;
            }
            else out += per;
            if(c+1 < N){
                board[r][c+1] += per;
                sum += per;
            }
            else out += per;
            per = y*0.01;
            if(r-1 >= 0 && c+1 < N){
                board[r-1][c+1] += per;
                sum += per;
            }
            else out += per;
            if(r-1 >= 0 && c-1 >= 0){
                board[r-1][c-1] += per;
                sum += per;
            }
            else out += per;
            per = y*0.1;
            if(r+1 < N && c-1 >= 0){
                board[r+1][c-1] += per;
                sum += per;
            }
            else out += per;
            if(r+1 < N && c+1 < N){
                board[r+1][c+1] += per;
                sum += per;
            }
            else out += per;
            per = y*0.05;
            if(r+2 < N){
                board[r+2][c] += per;
                sum += per;
            }
            else out += per;
            if(r+1 < N) board[r+1][c] += (board[r][c] - (sum + out));
            else out += (board[r][c] - (sum + out));
            board[r][c] = 0;
            total_out += out;
            break;
        //오른쪽
        case 2:
            per = y*0.02;
            if(r-2 >= 0){
                board[r-2][c] += per;
                sum += per;
            }
            else out += per;
            if(r+2 < N) {
                board[r+2][c] += per;
                sum += per;
            }
            else out += per;
            per = y*0.07;
            if(r-1 >= 0){
                board[r-1][c] += per;
                sum += per;
            }
            else out += per;
            if(r+1 < N){
                board[r+1][c] += per;
                sum += per;
            }
            else out += per;
            per = y*0.01;
            if(r-1 >= 0 && c-1 >= 0){
                board[r-1][c-1] += per;
                sum += per;
            }
            else out += per;
            if(r+1 < N && c-1 >= 0){
                board[r+1][c-1] += per;
                sum += per;
            }
            else out += per;
            per = y*0.1;
            if(r-1 >= 0 && c+1 < N){
                board[r-1][c+1] += per;
                sum += per;
            }
            else out += per;
            if(r+1 < N && c+1 < N){
                board[r+1][c+1] += per;
                sum += per;
            }
            else out += per;
            per = y*0.05;
            if(c+2 < N){
                board[r][c+2] += per;
                sum += per;
            }
            else out += per;
            if(c+1 < N) board[r][c+1] += (board[r][c] - (sum + out));
            else out += (board[r][c] - (sum + out));
            board[r][c] = 0;
            total_out += out;
            break;
        //위쪽
        case 3:
            per = y*0.02;
            if(c-2 >= 0){
                board[r][c-2] += per;
                sum += per;
            }
            else out += per;
            if(c+2 < N) {
                board[r][c+2] += per;
                sum += per;
            }
            else out += per;
            per = y*0.07;
            if(c-1 >= 0){
                board[r][c-1] += per;
                sum += per;
            }
            else out += per;
            if(c+1 < N){
                board[r][c+1] += per;
                sum += per;
            }
            else out += per;
            per = y*0.01;
            if(r+1 < N && c+1 < N){
                board[r+1][c+1] += per;
                sum += per;
            }
            else out += per;
            if(r+1 < N && c-1 >= 0){
                board[r+1][c-1] += per;
                sum += per;
            }
            else out += per;
            per = y*0.1;
            if(r-1 >= 0 && c-1 >= 0){
                board[r-1][c-1] += per;
                sum += per;
            }
            else out += per;
            if(r-1 >= 0 && c+1 < N){
                board[r-1][c+1] += per;
                sum += per;
            }
            else out += per;
            per = y*0.05;
            if(r-2 >= 0){
                board[r-2][c] += per;
                sum += per;
            }
            else out += per;
            if(r-1 >= 0) board[r-1][c] += (board[r][c] - (sum + out));
            else out += (board[r][c] - (sum + out));
            board[r][c] = 0;
            total_out += out;
            break;
    }
}

void move_tornado(){
	//맨 처음 토네이도의 위치 = 가운데, 토네이도의 방향 = 좌
    int tc = N/2;
    int tr = N/2;
    int dir = 0;
    
    //토네이도 이동 -> 규칙 적용 => O(N^2)
    for(int i=1; i<=N; i++){
        for(int j=0; j<2; j++){
            for(int k=1; k<=i; k++){
            	//모래가 흩뿌려지는 y 위치
                int yr = tr+dr[dir];
                int yc = tc+dc[dir];
                
                //토네이도 이동
                tr = yr;
                tc = yc;
                
                //토네이도가 (0, 0)에 도달한 경우, 모래 흩뿌리고 종료한다
                if(i==N && k==N-1){
                    spread_sand(yr, yc, dir);
                    cout << total_out <<endl;
                    return;
                }
                
                //y값이 0이라면 흩뿌려도 변함 없으므로, 무시한다
                if(board[yr][yc] == 0) continue;
                
                //y 위치의 모래 흩뿌리기
                spread_sand(yr, yc, dir);
            }
            //방향 전환하기
            dir = (dir+1) % 4;
        }
    }
}

int main(int argc, const char * argv[]) {
    cin >> N;
    for(int i=0; i<N; i++){
        for(int j=0; j<N; j++){
            cin >> board[i][j];
        }
    }
    move_tornado();
    return 0;
}
//시간복잡도 = O(N^2), 공간복잡도 = O(N^2)
728x90
반응형