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

2 minute read

문제는 다음과 같다.

오늘은 백준20056번 마법사 상어와 파이어볼 문제를 풀어보도록 하자. 작년 하반기 S전자 기출문제로 예전에 나온 기출과 비슷한 유형의 문제다. 우선 fire_ball벡터로 파이어볼의 갯수와 정보들을 수집한다.

그 후 fire_ball_move 함수로 파이어볼의 움직임을 처리하는데, 여기서 중요한 개념인 Modulation 연산이 나온다. S전자의 단골유형 중 하나로써 꼭 익히고 가야한다. 만일 모듈레이션을 안할 경우, 즉 하나하나 계산을 한다면 시간초과가 날 것이다.

파이어볼의 움직임 연산이 끝나면 연산이 끝난 파이어볼은 벡터에서 빼준다. 그 후 처리된 파이어볼을 3차원 벡터에 push를 해준다.

fire_ball_check 함수에선 파이어볼이 2개 이상 들어간 것을 체크해준다. 만일 2개이상 들어가지 않았다면 그냥 fire_ball 벡터에 넣어주고 2개 이상이라면 문제 조건에 나온대로 처리를 해준다.

문제에 적힌대로 구현하면 답을 찾을 수 있는 문제였다.

                                     [코드]
/*
1. fire_ball에 담는다.
2. 모두 움직이고 map에 넣어서 체크
3. 체크 후 다 빼고 fire_ball에 넣기
4. K--
*/
#include <iostream>
#include <vector>
using namespace std;

struct info {
	int m;//질량
	int s;//속력
	int d;//방향
};
struct more_info {
	int r_m;//행
	int c_m;//열
	int m_m;//질량
	int s_m;//속력
	int d_m;//방향
};
vector<info>map[51][51];
vector<more_info>fire_ball;
int dir[8][2] = { {-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1} };
int N, M, K,a,b,c,d,e,answer = 0;

int sum() {
	for (int i = 0; i < fire_ball.size(); i++)answer += fire_ball[i].m_m;
	return answer;
}

void fire_ball_check() {
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			if (map[i][j].size() >= 2) {//만약 파이어볼이 2개 이상?
				int even_cnt = 0, odd_cnt = 0, m_cnt = 0, s_cnt = 0;
				for (int k = 0; k < map[i][j].size(); k++) { //파이어볼이 있는 곳 처리
					m_cnt += map[i][j][k].m;//질량합
					s_cnt += map[i][j][k].s;//속력합

					if (map[i][j][k].d % 2 == 0) {//만일 방향이 짝수?
						even_cnt++;
					}
					else {//홀수?
						odd_cnt++;
					}
				}
				int check_m = m_cnt / 5;
				int check_s = s_cnt / map[i][j].size();
				if (check_m > 0) {
					if (even_cnt == map[i][j].size() || odd_cnt == map[i][j].size()) { //만약 다 짝수 or 홀수

						fire_ball.push_back({ i,j,check_m, check_s,0 });
						fire_ball.push_back({ i,j,check_m, check_s,2 });
						fire_ball.push_back({ i,j,check_m, check_s,4 });
						fire_ball.push_back({ i,j,check_m, check_s,6 });
					}
					else {// 그게 아니면 ?
						fire_ball.push_back({ i,j,check_m, check_s,1 });
						fire_ball.push_back({ i,j,check_m, check_s,3 });
						fire_ball.push_back({ i,j,check_m, check_s,5 });
						fire_ball.push_back({ i,j,check_m, check_s,7 });
					}
				}
			}
			else if(map[i][j].size() == 1){ //파이어볼 하나
				fire_ball.push_back({ i,j,map[i][j][0].m,map[i][j][0].s,map[i][j][0].d });
			}
			map[i][j].clear();
		}
	}
}

void fire_ball_move() {
	for (int i = fire_ball.size() - 1; i >= 0; i--) {
		//움직인다.
		int my = fire_ball[i].r_m + dir[fire_ball[i].d_m][0] * fire_ball[i].s_m;
		int mx = fire_ball[i].c_m + dir[fire_ball[i].d_m][1] * fire_ball[i].s_m;

		//만약 경계를 넘었으면
		if (my > N) {
			my %= N;
		}
		if (my < 1) {
			my = N - (abs(my) % N);
		}
		if (mx > N) {
			mx %= N;
		}
		if (mx < 1) {
			mx = N - (abs(mx) % N);
		}
		map[my][mx].push_back({ fire_ball[i].m_m, fire_ball[i].s_m, fire_ball[i].d_m });
	}
	fire_ball.clear();
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin >> N >> M >> K;//맵크기, 파이어볼 갯수, 실행횟수

	for (int i = 0; i < M; i++) {
		cin >> a >> b >> c >> d >> e;
		fire_ball.push_back({ a,b,c,d,e });
	}

	while (K--) {
		fire_ball_move();
		fire_ball_check();
	}
	cout << sum();
}