문제
낚시왕이 상어 낚시를 하는 곳은 크기가 R×C인 격자판으로 나타낼 수 있다. 격자판의 각 칸은 (r, c)로 나타낼 수 있다. r은 행, c는 열이고, (R, C)는 아래 그림에서 가장 오른쪽 아래에 있는 칸이다. 칸에는 상어가 최대 한 마리 들어있을 수 있다. 상어는 크기와 속도를 가지고 있다.
낚시왕은 처음에 1번 열의 한 칸 왼쪽에 있다. 다음은 1초 동안 일어나는 일이며, 아래 적힌 순서대로 일어난다. 낚시왕은 가장 오른쪽 열의 오른쪽 칸에 이동하면 이동을 멈춘다.
- 낚시왕이 오른쪽으로 한 칸 이동한다.
- 낚시왕이 있는 열에 있는 상어 중에서 땅과 제일 가까운 상어를 잡는다. 상어를 잡으면 격자판에서 잡은 상어가 사라진다.
- 상어가 이동한다.
상어는 입력으로 주어진 속도로 이동하고, 속도의 단위는 칸/초이다. 상어가 이동하려고 하는 칸이 격자판의 경계를 넘는 경우에는 방향을 반대로 바꿔서 속력을 유지한채로 이동한다.
왼쪽 그림의 상태에서 1초가 지나면 오른쪽 상태가 된다. 상어가 보고 있는 방향이 속도의 방향, 왼쪽 아래에 적힌 정수는 속력이다. 왼쪽 위에 상어를 구분하기 위해 문자를 적었다.
상어가 이동을 마친 후에 한 칸에 상어가 두 마리 이상 있을 수 있다. 이때는 크기가 가장 큰 상어가 나머지 상어를 모두 잡아먹는다.
낚시왕이 상어 낚시를 하는 격자판의 상태가 주어졌을 때, 낚시왕이 잡은 상어 크기의 합을 구해보자.
입력
첫째 줄에 격자판의 크기 R, C와 상어의 수 M이 주어진다. (2 ≤ R, C ≤ 100, 0 ≤ M ≤ R×C)
둘째 줄부터 M개의 줄에 상어의 정보가 주어진다. 상어의 정보는 다섯 정수 r, c, s, d, z (1 ≤ r ≤ R, 1 ≤ c ≤ C, 0 ≤ s ≤ 1000, 1 ≤ d ≤ 4, 1 ≤ z ≤ 10000) 로 이루어져 있다. (r, c)는 상어의 위치, s는 속력, d는 이동 방향, z는 크기이다. d가 1인 경우는 위, 2인 경우는 아래, 3인 경우는 오른쪽, 4인 경우는 왼쪽을 의미한다.
두 상어가 같은 크기를 갖는 경우는 없고, 하나의 칸에 둘 이상의 상어가 있는 경우는 없다.
출력
낚시왕이 잡은 상어 크기의 합을 출력한다.
예제 입력 1
4 6 8
4 1 3 3 8
1 3 5 2 9
2 4 8 4 1
4 5 0 1 4
3 3 1 2 7
1 5 8 4 3
3 6 2 1 2
2 2 2 3 5
예제 출력 1
22
각 칸의 왼쪽 아래에 적힌 수는 속력, 오른쪽 아래는 크기, 왼쪽 위는 상어를 구분하기 위한 문자이다. 오른쪽 위에 ❤️는 낚시왕이 잡은 물고기 표시이다.
초기 상태
1초
2초 (E번 상어는 B번에게 먹혔다)
3초
4초
5초
6초
예제 입력 2
100 100 0
예제 출력 2
0
예제 입력 3
4 5 4
4 1 3 3 8
1 3 5 2 9
2 4 8 4 1
4 5 0 1 4
예제 출력 3
22
예제 입력 4
2 2 4
1 1 1 1 1
2 2 2 2 2
1 2 1 2 3
2 1 2 1 4
예제 출력 4
4
알고리즘 분류
- 구현
- 시뮬레이션
풀이
상어의 정보를 담는 구조체를 선언하고, 상어 구조체를 담는 벡터를 선언해서 M마리의 상어를 담는다.
낚시왕은 총 C번 이동하게 되며, 격자 밖으로 벗어나면 반복문이 종료된다.
낚시왕이 상어를 잡을 때, 격자에는 상어가 무조건 1마리만 존재한다. 이 상어를 잡았다면 isLive라는 boolean형 변수를 false로 바꿔줘 죽었다는 표시를 해서, 앞으로 isLive가 false인 상어는 더 이상 체크하지 않도록 한다.
상어를 잡았으면 상어의 크기를 더해주고, 살아있는 모든 상어를 움직이게 하기 위해 우선 격자를 전부 비워 준다.
그리고 M마리의 상어 중 살아 있는 상어들을 이동시키고 상어가 이동한 격자에 1을 더해 준다.
여기서 이동할 때, S를 1씩 줄여가면서 이동시키면 시간 초과가 나므로, 몇만큼 이동해야 원래 위치에서 원래 방향을 바라보고 있는 지 확인한다.
상어가 위아래로 이동할 때는 (R-1)*2만큼 이동했다면 원래 위치에서 원래 방향을 바라보게 되고, 좌우로 이동할 때는 (C-1)*2만큼 이동했다면 원래 위치에서 원래 방향을 바라보게 된다.
이렇게 S를 줄여주면 S가 아무리 많아도 (R-1)*2 혹은 (C-1)*2보다는 작게 되므로 이동할 때 걸리는 시간을 크게 줄일 수 있다.
상어를 다 이동시켰다면 격자 중에 상어가 2마리 이상 존재하는 격자가 있을 것이다. 이 격자에서 가장 크기가 큰 상어를 제외한 나머지를 다 죽인다.(isLive = false)
이 모든 과정을 낚시왕이 격자를 벗어날 때까지 반복하고, 그 과정에서 잡아들이는 상어의 크기를 모두 더해주면 된다.
코드
#include <iostream>
#include <string>
#include <cmath>
#include <set>
#include <unordered_map>
#include <stack>
#include <queue>
#include <vector>
#include <algorithm>
#define FIRST cin.tie(NULL); cout.tie(NULL); ios::sync_with_stdio(false);
#define MAX 105
#define LL long long
#define INF 1e9
using namespace std;
struct SHARK {
int R, C, S, D, Z;
bool isLive = true;
};
int R, C, M;
vector<SHARK> Shark;
int Fisher = 0;
int MAP[MAX][MAX];
int moveY[5] = { 0,-1,1,0,0 };
int moveX[5] = { 0,0,0,1,-1 };
int answer = 0;
void Shark_Moving(int IDX) {
int Len = Shark[IDX].S;
int Dir = Shark[IDX].D;
int Y = Shark[IDX].R;
int X = Shark[IDX].C;
if ((Dir == 1) || (Dir == 2)) { // 상어가 위아래로 움직이는 경우
Len %= ((R - 1) * 2);
}
else if ((Dir == 3) || (Dir == 4)) { // 상어가 좌우로 움직이는 경우
Len %= ((C - 1) * 2);
}
while (Len--) {
int nextY = Y + moveY[Dir];
int nextX = X + moveX[Dir];
if ((nextY < 1) || (nextY > R) || (nextX < 1) || (nextX > C)) {
if (Dir == 1) {
Dir = 2;
}
else if (Dir == 2) {
Dir = 1;
}
else if (Dir == 3) {
Dir = 4;
}
else if (Dir == 4) {
Dir = 3;
}
nextY = Y + moveY[Dir];
nextX = X + moveX[Dir];
}
Y = nextY;
X = nextX;
};
Shark[IDX].D = Dir;
Shark[IDX].R = Y;
Shark[IDX].C = X;
MAP[Y][X]++;
}
void Shark_Eating(int Y, int X) {
int Big = -1;
int Big_IDX = 0;
for (int i = 1; i <= M; i++) {
if ((Shark[i].isLive) && (Shark[i].R == Y) && (Shark[i].C == X)) {
if (Big < Shark[i].Z) {
Big = Shark[i].Z;
Big_IDX = i;
}
}
}
for (int i = 1; i <= M; i++) {
if ((Shark[i].isLive) && (Shark[i].R == Y) && (Shark[i].C == X)) {
if (Big != Shark[i].Z) {
Shark[i].isLive = false;
}
}
}
MAP[Y][X] = 1;
}
int main() {
FIRST
cin >> R >> C >> M;
Shark.resize(M + 1);
for (int i = 1; i <= M; i++) {
int R, C, S, D, Z;
cin >> Shark[i].R >> Shark[i].C >> Shark[i].S >> Shark[i].D >> Shark[i].Z;
MAP[Shark[i].R][Shark[i].C]++;
}
while (1) {
Fisher++; // 1. 낚시왕이 오른쪽으로 한 칸 이동한다.
if (Fisher > C) { // 낚시왕은 가장 오른쪽 열의 오른쪽 칸에 이동하면 이동을 멈춘다.
break;
}
for (int i = 1; i <= R; i++) { // 2. 땅과 가장 가까이에 있는 상어를 잡는다고 하였으므로, 1번째 행에서부터 상어를 찾는다.
if (MAP[i][Fisher] == 1) {
int Cur_Shark; // 상어의 인덱스
for (int j = 1; j <= M; j++) {
if ((Shark[j].isLive) && (Shark[j].R == i) && (Shark[j].C == Fisher)) {
Cur_Shark = j;
break;
}
}
Shark[Cur_Shark].isLive = false; // 상어를 잡고
answer += Shark[Cur_Shark].Z; // 상어를 잡았으면 상어의 크기를 더한다.
break;
}
}
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++) {
MAP[i][j] = 0;
}
}
for (int i = 1; i <= M; i++) {
if (Shark[i].isLive) { // 살아있는 상어들은 정해진 속력과 이동 방향대로 움직인다.
int nextY = Shark[i].R + moveY[Shark[i].D];
int nextX = Shark[i].C + moveX[Shark[i].D];
Shark_Moving(i);
}
}
// 상어가 2마리 이상 있는 격자는 크기가 가장 큰 상어가 나머지 상어를 모두 잡아먹는다.
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++) {
if (MAP[i][j] >= 2) {
Shark_Eating(i, j);
}
}
}
};
cout << answer << "\n";
return 0;
}
'BOJ > Gold' 카테고리의 다른 글
[BOJ/Gold 2] 백준 17780 새로운 게임(C++) (0) | 2022.01.18 |
---|---|
[BOJ/Gold 2] 백준 16939 2×2×2 큐브(C++) (0) | 2022.01.17 |
[BOJ/Gold 2] 백준 5577 RBY팡!(C++) (0) | 2022.01.17 |
[BOJ/Gold 2] 백준 3101 토끼의 이동(C++) (0) | 2022.01.17 |
[BOJ/Gold 3] 백준 1111 IQ Test(C++) (0) | 2022.01.16 |