본문 바로가기
Develop/백준 (Cpp)

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

by Tarra 2023. 11. 25.

20056번 : 마법사 상어와 파이어볼


문제)

어른 상어가 마법사가 되었고, 파이어볼을 배웠다.

마법사 상어가 크기가 N×N인 격자에 파이어볼 M개를 발사했다. 가장 처음에 파이어볼은 각자 위치에서 이동을 대기하고 있다. i번 파이어볼의 위치는 (ri, ci), 질량은 mi이고, 방향은 di, 속력은 si이다. 위치 (r, c)는 r행 c열을 의미한다.

격자의 행과 열은 1번부터 N번까지 번호가 매겨져 있고, 1번 행은 N번과 연결되어 있고, 1번 열은 N번 열과 연결되어 있다.

파이어볼의 방향은 어떤 칸과 인접한 8개의 칸의 방향을 의미하며, 정수로는 다음과 같다.

 

 

마법사 상어가 모든 파이어볼에게 이동을 명령하면 다음이 일들이 일어난다.

  1. 모든 파이어볼이 자신의 방향 di로 속력 si칸 만큼 이동한다.
    • 이동하는 중에는 같은 칸에 여러 개의 파이어볼이 있을 수도 있다.
  2. 이동이 모두 끝난 뒤, 2개 이상의 파이어볼이 있는 칸에서는 다음과 같은 일이 일어난다.
    1. 같은 칸에 있는 파이어볼은 모두 하나로 합쳐진다.
    2. 파이어볼은 4개의 파이어볼로 나누어진다.
    3. 나누어진 파이어볼의 질량, 속력, 방향은 다음과 같다.
      1. 질량은 ⌊(합쳐진 파이어볼 질량의 합)/5⌋이다.
      2. 속력은 ⌊(합쳐진 파이어볼 속력의 합)/(합쳐진 파이어볼의 개수)⌋이다.
      3. 합쳐지는 파이어볼의 방향이 모두 홀수이거나 모두 짝수이면, 방향은 0, 2, 4, 6이 되고, 그렇지 않으면 1, 3, 5, 7이 된다.
    4. 질량이 0인 파이어볼은 소멸되어 없어진다.

마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 구해보자.

 

 

 

입력 :

첫째 줄에 N, M, K가 주어진다.

둘째 줄부터 M개의 줄에 파이어볼의 정보가 한 줄에 하나씩 주어진다. 파이어볼의 정보는 다섯 정수 ri, ci, mi, si, di로 이루어져 있다.

서로 다른 두 파이어볼의 위치가 같은 경우는 입력으로 주어지지 않는다.

 

 

 

출력 :

마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 출력한다.

 

 

 

 

 

 

풀이)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// 20056. 마법사 상어와 파이어볼
#include <iostream>
#include <vector>
 
using namespace std;
 
struct fireball
{
    int mass;
    int speed;
    int direction;
 
    fireball()
        : mass(0), speed(0), direction(0)
    {}
 
    fireball(int m, int s, int d)
        : mass(m), speed(s), direction(d)
    {}
};
 
int n, m, k;
vector<vector<vector<fireball>>> vec;
 
int dx[] = { -1-101110-1 };
int dy[] = { 01110-1-1-1 };
 
 
// 파이어볼이 격자를 벗어나지 않도록 한다.
// 1번열과 n번열은 연결되어 있다.
void in_range(int& x, int& y)
{
    if (0 >= x)
    {
        while (!(1 <= x && x <= n))
        {
            x += n;
        }
    }
 
    if (0 >= y)
    {
        while (!(1 <= y && y <= n))
        {
            y += n;
        }
    }
 
    if (x > n)
    {
        while (!(1 <= x && x <= n))
        {
            x -= n;
        }
    }
 
    if (y > n)
    {
        while (!(1 <= y && y <= n))
        {
            y -= n;
        }
    }
}
 
// 모든 파이어볼이 자신의 방향 d로 속력 s칸 만큼 이동
void phase1()
{
    vector<vector<vector<fireball>>> temp (n + 1vector<vector<fireball>>(n + 1));
 
    for (int i = 1; i < n + 1; i++)
    {
        for (int j = 1; j < n + 1; j++)
        {
            for (int k = 0; k < vec[i][j].size(); k++)
            {
                fireball fb = vec[i][j][k];
                int nx = i + dx[fb.direction] * fb.speed;
                int ny = j + dy[fb.direction] * fb.speed;
 
                in_range(nx, ny);
 
                temp[nx][ny].push_back(fb);
            }
        }
    }
 
    vec = temp;
}
 
// 이동이 모두 끝난 뒤.
void phase2()
{
    vector<vector<vector<fireball>>> temp_vec(n + 1vector<vector<fireball>>(n + 1));
 
    for (int i = 1; i < n + 1; i++)
    {
        for (int j = 1; j < n + 1; j++)
        {
            bool flag = 0;
 
            fireball temp;
 
            // 2개 이상의 파이어볼이 있는 칸
            if (vec[i][j].size() > 1)
            {
                // 모두 홀수인지 짝수인지 확인
                bool bin = vec[i][j][0].direction % 2;
 
                for (int k = 0; k < vec[i][j].size(); k++)
                {
                    temp.mass += vec[i][j][k].mass;
                    temp.speed += vec[i][j][k].speed;
 
                    if (bin != vec[i][j][k].direction % 2) flag = 1;
                }
            }
            // 파이어볼이 1개인 경우
            else if (vec[i][j].size() == 1)
            {
                // 질량이 0인 파이어볼은 소멸
                if (vec[i][j][0].mass == 0continue;
                temp_vec[i][j].push_back(vec[i][j][0]);
                continue;
            }
            else continue;
 
            int idx = 0;
            if (flag) idx++;
 
            int _mass = temp.mass / 5;
            int _speed = temp.speed / vec[i][j].size();
 
            // 질량이 0인 파이어볼은 소멸
            if (_mass == 0continue;
 
            // 파이어볼이 4개로 쪼개진다.
            for (int k = 0; k < 4; k++)
            {
                temp_vec[i][j].push_back(fireball({ _mass, _speed, idx }));
                idx += 2;
            }
        }
    }
 
    vec = temp_vec;
}
 
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n >> m >> k;
 
    vec.resize(n + 1vector<vector<fireball>> (n + 1));
 
    int r, c, _m, s, d;
    for (int i = 0; i < m; i++)
    {
        cin >> r >> c >> _m >> s >> d;
        vec[r][c].push_back({ _m, s, d });
    }
 
    while (k--)
    {
        phase1();
        phase2();
    }
 
    // 남아있는 파이어볼의 질량을 구한다.
    int ans = 0;
    for (int i = 1; i < n + 1; i++)
    {
        for (int j = 1; j < n + 1; j++)
        {
            int iLen = vec[i][j].size();
            for (int k = 0; k < iLen; k++)
            {
                ans += vec[i][j][k].mass;
            }
        }
    }
 
    cout << ans;
 
    return 0;
}
 
cs

 

 


출처 : https://www.acmicpc.net/problem/20056 

 

20056번: 마법사 상어와 파이어볼

첫째 줄에 N, M, K가 주어진다. 둘째 줄부터 M개의 줄에 파이어볼의 정보가 한 줄에 하나씩 주어진다. 파이어볼의 정보는 다섯 정수 ri, ci, mi, si, di로 이루어져 있다. 서로 다른 두 파이어볼의 위치

www.acmicpc.net