백준의 낚시왕(17143) 문제이다.
[ 문제 바로가기 ]
[ 문제풀이 ]
1) 먼저 문제를 풀기 전에 변수를 어떤 자료형으로 선언했는지부터 알아보고, 그 자료형을 선택한 이유와 어떻게 사용되는지
부터 알아본 후에 문제를 풀어보자.
먼저 본인은 상어들의 정보를 한번에 관리하기 위해서 구조체를 하나 만들어 주었다. 구조체에서 관리해주는 변수로는
{ 상어의(x,y)좌표 , 상어의 속력 , 상어의 진행방향 , 상어의 크기 , 상어의 번호 }
이렇게 총 6개의 자료를 관리해 주었다.
또한, 상어들의 정보를 모두 저장해줄 vector를 하나 생성해서 저장해 주었다.
또 하나는 2차원 맵을 int형이 아닌 vector<int> 형으로 생성해 주었다. 이렇게 만들어준 이유는 해당좌표에 존재하는
상어의 번호(구조체에서 '상어의 번호')를 저장해 주기 위함이었다.
중간에 보면 상어가 서로 같은 정점에서 만나게 될 경우 더 큰 상어가 작은 상어를 잡아 먹는다는 부분이 있다.
즉, MAP[x][y]의 Size가 2이상일 경우, 특정 기준으로 정렬을 통해서 더 큰 상어만이 살아남도록 구현해 주었다.
자세한건 밑에서 구체적으로 알아보도록 하자.
2) 먼저 문제의 진행방식을 크게 3단계로 나눌 수 있다.
1. 사람이 낚시하기
2. 상어 움직이기
3. 움직인 상어들 중에서 서로 겹치는 상어를 죽여주기.
단계별로 알아보도록 하자.
먼저 사람이 낚시를 하는 과정이다.
낚시를 할 때에는 현재 열이 몇 열인지만 알고 있으면 된다. 현재 열의 1행부터 ~ R행까지 진행하면서 MAP의 Size가
1인 정점을 만나게 되면, 그 상어를 죽여주고 상어의 Size를 더해주면 된다.
그런데, MAP의 Size가 1이라는 것은 상어가 그 좌표에 존재한다는 것은 알겠는데, 그 상어가 어떤 상어인지 어떻게 알까?
이를 위해서 MAP을 vector<int> 형으로 선언해 준 것이다. 위에서도 말했듯이, MAP에는 해당 좌표에 존재하는 상어의
번호가 원소로 들어가 있다.
즉 예를들어서 MAP[x][y]의 vector 상태가 { 0 } 이라면, 0번 상어가 해당 좌표에 존재한다는 것이다.
우리는 이를 통해서 해당 상어를 죽여줄 수 있다. 죽이는 과정은 간단하다.
해당 상어의 크기를 0으로 만들어 주었다.
두번쨰로는 상어가 움직이는 과정이다.
이 과정은 사실 수 많은 방법이 존재할 수 있지만, 본인은 정말 가장 간단하게 한칸한칸씩 움직여 주었다.
맵의 범위를 벗어날 때 마다 적절하게 값을 변경해 주면서 방향을 바꿔주었다.
여기서 주의해야할 것은, 상어를 모두 움직였다면 MAP에서도 바꿔주어야 하고, 해당 상어의 (x, y)와 진행방향 또한
바꿔줘야 한다는 것을 주의해주자.
사실 처음에 이렇게 구현을 해서 pass를 받았으나, 재채점 이 후 시간초과가 나서 이 부분을 수정해보았다.
문제의 제한시간이 1초인데, 최악의 경우 최대 상어수가 10000마리인데, 10000마리가 1000의 스피드, 즉 1000칸씩
움직이게 될 경우 10000 * 1000 = 10^7 만큼의 시간이 걸리고, 사람이 1열부터 C열까지 움직일 때 마다 해야 하니
10^7 * 10^2(C의 최대값) 이 되어 최악의 경우 10^9만큼의 연산을 하게 된다.
당연히 제한시간 1초 내에 해결될 수가 없다.
따라서 이 부분을 정말 간단하게 모듈러 연산으로 한번 바꿔보았다. 이 부분에 대해서 알아보자.
하나의 상어가, 자신의 원래 방향을 그대로 유지한채 자기 자리로 돌아오기 위해서는 몇 칸이 필요할까 ?
답부터 말하자면, "위아래로 움직이는 상어의 경우 : (R-1)*2 , 좌우로 움직이는 상어의 경우 : (C-1)*2" 가 된다.
정말 간단하게 상어의 진행방향과 번호만 나타낸 것이다. 맵의 R = 4, C = 5 이다. 맵의 가장 왼쪽 위를 (1,1)로 생각하겠다.
먼저, 1번 2번 상어를 보자. 1번 상어가 진행방향을 오른쪽으로 가지고 다시 (2, 1)로 돌아오기 위해서는 몇 칸을 움직여
주어야 할까? 직접 하나하나 세보면 알겠지만 8칸을 움직이면 된다. 방향이 반대일 때 제자리에 도착하지만, 어차피
이 다음에 움직일 때 바로 방향이 전환되기 때문에 상관이 없다.
2번 상어의 경우는 ?? 4번 움직이게 되면, 똑같은 자리에 진행방향 반대일 것이고, 8번 움직이면, 처음과 똑같은 자리에
같은 진행방향을 가진 채로 도착하게 된다. 즉, 좌우로 움직이는 상어들에 경우 (C - 1) * 2번을 움직이게 되면
처음 자리에 같은 진행방향을 가진 채 도착을 하게 된다.
3번, 4번의 경우도 똑같다. (R - 1) * 2번을 움직이게 되면 처음 자리에 같은 진행방향을 가진 채 도착하게 된다.
그렇다면 ! 위의 그림에서 2번상어가 9칸을 움직일 수 있다면 어디에 가있게 될까? 아마 한칸 왼쪽칸인 (3, 2)에 위치하게
될 것이다. 이거를 굳이, 9칸을 움직여 볼 필요가 없다. 왜 ? 우리는 8칸을 움직이면 처음과 같은 상태라는 것을 알기 때문
이다. 즉, 9 % 8 한 칸 만큼만 움직여 주면 된다. 그럼 20칸을 움직이게되면 ? 20 % 8 = 4가 되고, 4칸만 움직여 주게 되면
20칸을 움직인 것과 똑같은 결과를 가져오게 된다. 본인은 이렇게 모듈러 연산을 통해서 최대 (R - 1) * 2칸 혹은
(C - 1) * 2칸만 움직이도록 구현해 주었다.
이 부분을 이렇게 수정하니 시간초과를 받지 않고 pass를 받을 수 있었다.
세번째로는 상어가 서로 잡아먹는 과정이다.
우리는 상어가 서로 같은칸에 있음을 어떻게 알 수 있을까?? 아마 해당 칸의 MAP의 Size가 2 이상일 것이다.
즉, 2이상인 정점에 대해서는 본인은 상어의 크기순으로 정렬을 해 주었다.
정렬을 하게 되면 0번째 Index에 크기가 가장 큰 상어가 존재할 것이고 그 이후에 점차 작아지는 순으로 정렬이 되어
있을 것이다. 이후에, 0번째 Index를 제외한 모든 상어의 상태를 false로 바꿔주고 MAP을 비운 후에 0번째 Index에
존재하는 상어의 번호만 다시 해당 좌표에 넣어주면 된다.
3) 문제를 있는 그대로 구현을 하다보면 어렵지 않게 해결할 수 있을 것이다.
[ 소스코드 ]
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 190 191 192 193 194 195 | #include<iostream> #include<vector> #include<algorithm> #define endl "\n" #define MAX 100 + 1 using namespace std; struct Shark_Info { int R; int C; int Speed; int Direct; int Size; int Num; }; int R, C, M, Answer; vector<int> MAP[MAX][MAX]; vector<Shark_Info> Shark; int dx[] = { 0, -1, 1, 0, 0 }; int dy[] = { 0, 0, 0, 1, -1 }; bool Standard(int A, int B) { if (Shark[A].Size > Shark[B].Size) return true; return false; } void Input() { cin >> R >> C >> M; for (int i = 0; i < M; i++) { int r, c, s, d, z; cin >> r >> c >> s >> d >> z; Shark.push_back({ r,c,s,d,z,i }); MAP[r][c].push_back(i); } } bool Check() { for (int i = 0; i < Shark.size(); i++) { if (Shark[i].Size != 0) return false; } return true; } void Fishing(int Idx) { for (int i = 1; i <= R; i++) { if (MAP[i][Idx].size() != 0) { Answer = Answer + Shark[MAP[i][Idx][0]].Size; Shark[MAP[i][Idx][0]].Size = 0; MAP[i][Idx].clear(); break; } } } void Move_Shark() { for (int i = 0; i < Shark.size(); i++) { if (Shark[i].Size == 0) continue; int x = Shark[i].R; int y = Shark[i].C; MAP[x][y].clear(); } for (int i = 0; i < Shark.size(); i++) { if (Shark[i].Size == 0) continue; int x = Shark[i].R; int y = Shark[i].C; int Direct = Shark[i].Direct; int Speed = Shark[i].Speed; if (Direct == 1 || Direct == 2) { int Rotate = (R - 1) * 2; if (Speed >= Rotate) Speed = Speed % Rotate; for (int j = 0; j < Speed; j++) { int nx = x + dx[Direct]; int ny = y + dy[Direct]; if (nx < 1) { Direct = 2; nx = nx + 2; } if (nx > R) { Direct = 1; nx = nx - 2; } x = nx; y = ny; } } else { int Rotate = (C - 1) * 2; if (Speed >= Rotate) Speed = Speed % Rotate; for (int j = 0; j < Speed; j++) { int nx = x + dx[Direct]; int ny = y + dy[Direct]; if (ny < 1) { Direct = 3; ny = ny + 2; } if (ny > C) { Direct = 4; ny = ny - 2; } x = nx; y = ny; } } Shark[i].R = x; Shark[i].C = y; Shark[i].Direct = Direct; MAP[x][y].push_back(i); } } void Kill_Shark() { for (int i = 1; i <= R; i++) { for (int j = 1; j <= C; j++) { if (MAP[i][j].size() > 1) { sort(MAP[i][j].begin(), MAP[i][j].end(), Standard); int Live_Index = MAP[i][j][0]; for (int k = 1; k < MAP[i][j].size(); k++) { Shark[MAP[i][j][k]].Size = 0; Shark[MAP[i][j][k]].R = -1; Shark[MAP[i][j][k]].C = -1; } MAP[i][j].clear(); MAP[i][j].push_back(Shark[Live_Index].Num); } } } } void Solution() { if (M == 0) { cout << 0 << endl; return; } for (int i = 1; i <= C; i++) { if (Check() == true) break; Fishing(i); Move_Shark(); Kill_Shark(); } cout << Answer << endl; } void Solve() { Input(); Solution(); } int main(void) { ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); //freopen("Input.txt", "r", stdin); Solve(); return 0; } | cs |
'[ BOJ Code ] > # BOJ -' 카테고리의 다른 글
[ 백준 7453 ] 합이 0인 네 정수 (C++) (14) | 2019.09.17 |
---|---|
[ 백준 17406 ] 배열 돌리기4 (C++) (2) | 2019.09.06 |
[ 백준 4920 ] 테트리스 게임 (C++) (2) | 2019.08.30 |
[ 백준 1907 ] 탄소 화합물 (C++) (0) | 2019.08.29 |
[ 백준 17281 ] 야구 (C++) (13) | 2019.08.29 |