[C++] 백준 20056 : 마법사 상어와 파이어볼

Kim Nahyeong·2022년 9월 30일
0

백준

목록 보기
141/157

#include <iostream>
#include <vector>

using namespace std;

struct fireball{
  int x, y;
  int m;
  int d;
  int s;
};

vector<fireball> map[51][51];
vector<fireball> fireballs; // fireball 정보를 따로 관리하는 것이 좋음. map 전체 탐색하기 힘들기 때문에(4중 for문)

int N, M, K; // K번 이동 명령
int r, c, m, d, s; // (r, c), 질량, 방향, 속력
int ans = 0;

// 격자 방향
int dx[8] = {-1, -1, 0, 1, 1, 1, 0, -1};
int dy[8] = {0, 1, 1, 1, 0, -1, -1, -1};

// 모든 파이어볼 이동
void move(){
  // map 초기화
  for(int i = 1; i <= N; i++){
    for(int j = 1; j <= N; j++){
      map[i][j].clear();
    }
  }

  // 모든 파이어볼 이동 수행 및 맵에 저장
  for(int i = 0; i < fireballs.size(); i++){
    fireball fb = fireballs[i];
    int cm = fb.m, cd = fb.d, cs = fb.s;
    int nx = fb.x, ny = fb.y;
    int cx = fb.x, cy = fb.y;
    
    // 속력만큼 이동 해당 방향으로 이동
    for(int j = 0; j < cs; j++){
      nx += dx[cd];
      ny += dy[cd];

      // 이렇게 특정 조건이면 값 변해야할 때 else if 사용이 아닌 모두 if문을 따로 사용해서 모든 조건을 체크하는 것이 옳다.
      if(nx < 1){
        nx = N;
      }
      if(ny < 1){
        ny = N;
      }
      if(nx > N){
        nx = 1;
      }
      if(ny > N){
        ny = 1;
      }
    }

    map[nx][ny].push_back({nx, ny, cm, cd, cs});
  }
}

// 이동이 모두 끝난 뒤, 2개 이상의 파이어볼이 있는 칸에서는 다음과 같은 일이 일어난다.
void split(){
  // 임시로 파이어볼 이동한 위치 저장하는 벡터
  vector<fireball> temp;

  for(int i = 1; i <= N; i++){
    for(int j = 1; j <= N; j++){
      // 변수 여기에 선언해야 모든 경우에 자동 초기화
      int msum = 0, ssum = 0;
      bool evenFlag = false, oddFlag = false;
      
      // 파이어볼 하나만 존재(분할x)
      if(map[i][j].size() == 1){
        temp.push_back(map[i][j][0]); // 이렇게 그대로 넣어줄 수 있음. 변수 안 빼도 됨
      } else if(map[i][j].size() >= 2){
        for(int k = 0; k < map[i][j].size(); k++){
          msum += map[i][j][k].m; // 질량합
          ssum += map[i][j][k].s; // 속력합

          // 방향 짝홀 탐색
          if(map[i][j][k].d % 2 == 0){
            evenFlag = true;
          } else {
            oddFlag = true;
          }
        }

        msum /= 5;
        ssum = ssum / map[i][j].size();

        // 질량이 0인 파이어볼은 소멸되어 없어진다.
        if(msum == 0){
          continue;
        }

        // 모두 홀수이거나 짝수
        if((evenFlag && !oddFlag) || (!evenFlag && oddFlag)){
          temp.push_back({i, j, msum, 0, ssum});
          temp.push_back({i, j, msum, 2, ssum});
          temp.push_back({i, j, msum, 4, ssum});
          temp.push_back({i, j, msum, 6, ssum});
        } else {
          temp.push_back({i, j, msum, 1, ssum});
          temp.push_back({i, j, msum, 3, ssum});
          temp.push_back({i, j, msum, 5, ssum});
          temp.push_back({i, j, msum, 7, ssum});
        }
      }
    }  
  }
  fireballs = temp; // 새로운 파이어볼 정보 저장
}

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

  for(int i = 0; i < M; i++){
    scanf("%d %d %d %d %d", &r, &c, &m, &s, &d); // 설명의 r, c, m, d, s 아님!
    fireballs.push_back({r, c, m, d, s}); // 파이어볼 정보 저장
  }

  // K번 이동
  for(int j = 0; j < K; j++){
    move();
    // 움직이고 나누고 반복 - 다 움직이고 난 다음에 split 하는 것 아님.
    split();
  }

  // 마지막 남아있는 파이어볼 질량의 합
  for(int i = 0; i < fireballs.size(); i++) {
    ans += fireballs[i].m;
  }

  printf("%d", ans);

  return 0;
}

진짜 독해가 중요하구나 느꼈고 관성적으로 하는 코딩의 무서움을 느낀 문제.

  • map vector만을 사용해서 문제를 풀려고 했는데 4중 for문을 사용해야해서 코드 가독성이 굉장히 떨어졌다. 그래서 fireballs 벡터를 만들어 따로 fireball 정보를 관리하도록 수정해서 풀었다.
    (이거 수정때문에 시간이 진짜 오래 걸림... map은 그때그때 초기화(clear)해서 fireballs vector만 관리하도록 생각하는 것이 어려웠다.)

  • 파이어볼들은 K번 움직이고 나서 분할되는 것이 아니라 움직이고 분할되고를 K번 반복하는 것이다.

  • 모든 조건을 만족해야하면 if-else if 사용이 아닌 각자 if문을 사용해서 모든 조건을 만족하도록 해야한다.

  • 모두 홀수이거나 짝수임을 탐색할 때는 flag를 사용하는 것이 좋다.

0개의 댓글