SWExpertAcademy의 밍이의 블록게임(4740 / D5) 문제이다.
[ 문제풀이 ]
맵에 존재하는 블럭들을 명령어에 따라 움직이고 내리고 올리고 하는 과정을 구현해야 하는 문제이다.
지금부터는 본인이 구현한 방법을 명령어별로 소개하겠다.
특별한 알고리즘이나 방법이 사용되는 것이 아니기 때문에, 간략한 설명과 코드 설명을 함께 진행하겠다.
1. Command 'U'
입력으로 주어지는 한 행의 블록 배열을 가장 아랫줄에 추가해야 하는 명령어이다.
단 ! 추가되지 않는 딱 한가지 경우가 있다.
바로 "모든 행에 하나 이상의 블록이 존재하는 경우" 이다.
간단하게 말해서 맵이 0열 ~ M - 1열까지 존재한다고 가정했을 때, 그 어느 한 열이라도, 0번째 행에 블럭이 존재한다면
이는 가장 윗줄에 블럭이 존재한다는 것을 의미하고, 더 이상 블록 배열을 가장 아랫줄에 추가할 수 없다는 것을
의미한다.
따라서, 위의 경우는 해당 명령을 무시하고 진행하면 된다.
1 2 3 4 5 6 7 8 9 10 | void Up(string NewLine) { for (int i = 0; i < M; i++) { if (MAP[0][i] != '#') return; } for (int i = 1; i < N; i++) memcpy(MAP[i - 1], MAP[i], sizeof(char)* M); for (int i = 0; i < M; i++) MAP[N - 1][i] = NewLine[i]; } | cs |
본인이 구현한 명령어 'U' 소스코드이다.
line3 ~ 6)을 보면, 모든 열을 탐색하면서, 0번행에 블럭이 있는 열이 하나라도 있다면, 명령어를 진행시키지 않고 그대로 return 시킴으로써 종료시키는 것을 확인할 수 있다.
그게 아니라면, 모든 행에 존재하는 블럭들을 한 줄씩 올려주면 된다. 이 과정이 line8) 에 있는 과정이다.
line9) 에서는 추가되는 새로운 행을 가장 마지막 행에 추가해주는 과정이다.
그런데 ! 이렇게 하면 새로운 행을 추가하고, 모든 블럭들을 한칸 위로 옮기는 과정을 진행하는 것은 맞지만, 새로운 행에 빈 칸이 있을 경우, 그 빈칸에 맞게 블럭들을 다시 떨궈줘야 하는 과정또한 진행해 주어야 한다.
이 과정은, 'D'명령어에서도 사용되기 떄문에, 다른 명령어들을 모두 소개한 후에 마지막에 설명하겠다.
2. Command 'L' & 'R'
블럭들을 왼쪽으로, 혹은 오른쪽으로 옮겨야 하는 명령어이다.
똑같은 방법으로 구현해서 2개의 명령어를 하나로 묶어서 설명하겠다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void Left() { for (int i = 0; i < N; i++) { for (int j = 1; j < M; j++) { if (MAP[i][j] == '#') continue; int ny = j - 1; while (ny >= 0 && MAP[i][ny] == '#') ny--; ny++; if (ny != j) { MAP[i][ny] = MAP[i][j]; MAP[i][j] = '#'; } } } } | cs |
본인이 구현한 명령어 'L'에 대한 코드이다.
블럭이 존재하는 좌표를 찾게 되면 움직일 수 없을 떄 까지 빈칸을 찾아서 계속해서 움직여 준다.
이 과정이 line10)에 나타나 있다. 움직일 수 없을 때 까지 반복하면서 좌표를 갱신해준다.
그래서 더 이상 움직일 수 없는 좌표를 찾았다면, 2가지 경우가 발생한다.
1. 애초에 움직일 수 없는 블럭이었는지
2. 움직일 수 있는 블럭이었는지
만약, 애초에 움직일 수 없는 블럭이었다면, "움직일 수 없을 때 까지 반복한 좌표가 기존의 좌표와 동일" 할 것이다.
그럼, 블럭의 갱신이 필요하지 않다.
반대로, 움직일 수 있는 블럭이라면, line13)과 같이 조건문에 부합해서 블럭의 갱신이 일어나게 된다.
3. Command 'D'
가장 크기가 큰 블록 집합을 없애야 하는 명령어 이다.
이 과정을 본인은 BFS탐색을 통해서 그 갯수를 파악해 주었다.
또한, 갯수를 파악하면서 동시에 "어떤 집합을 삭제해야 되는지"를 표현하기 위해서 Visit배열을 사용해 주었다.
Visit배열을 단순, true / false가 아닌, 1번부터 번호를 매기면서 탐색을 해주었다.
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 | void Down() { vector<int> V; memset(Visit, 0, sizeof(Visit)); int Num = 1; int Max_Block = 0; for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { if (MAP[i][j] == '#') continue; if (Visit[i][j] != 0) continue; int Result = BFS(i, j, MAP[i][j], Num); if (Result > Max_Block) { Max_Block = Result; V.clear(); V.push_back(Num); } else if (Result == Max_Block) { V.push_back(Num); } Num++; } } for (int i = 0; i < V.size(); i++) Erase(V[i]); } | cs |
4. 맵 정리하기
''U'명령어 혹은 'D'명령어 이후에는 맵을 재정리를 해줄 필요가 있다. 왜냐하면 중간중간 빈 공간이 존재할 수 있기 때문이다.
이 과정 또한, 위에서 설명한 'L' 과 'R'명령어와 동일한 방법으로 구현해 주었다.
[ 소스코드 ]
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 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | #include <iostream> #include <cstring> #include <string> #include <vector> #include <queue> #include <algorithm> #define endl "\n" #define MAX 35 using namespace std; int N, M, Q; int Visit[MAX][MAX]; char MAP[MAX][MAX]; vector<pair<char, string>> Cmd; int dx[] = { 0, 0, 1, -1 }; int dy[] = { 1, -1, 0, 0 }; void Print() { for (int i = 0; i < N; i++) { cout << MAP[i] << endl; } } void Initialize() { Cmd.clear(); memset(Visit, 0, sizeof(Visit)); } void Input() { cin >> N >> M >> Q; for (int i = 0; i < N; i++) { cin >> MAP[i]; } for (int i = 0; i < Q; i++) { char C; cin >> C; if (C == 'U') { string S; cin >> S; Cmd.push_back(make_pair(C, S)); } else Cmd.push_back(make_pair(C, "")); } } void Up(string NewLine) { for (int i = 0; i < M; i++) { if (MAP[0][i] != '#') return; } for (int i = 1; i < N; i++) memcpy(MAP[i - 1], MAP[i], sizeof(char)* M); for (int i = 0; i < M; i++) MAP[N - 1][i] = NewLine[i]; } void Left() { for (int i = 0; i < N; i++) { for (int j = 1; j < M; j++) { if (MAP[i][j] == '#') continue; int ny = j - 1; while (ny >= 0 && MAP[i][ny] == '#') ny--; ny++; if (ny != j) { MAP[i][ny] = MAP[i][j]; MAP[i][j] = '#'; } } } } void Right() { for (int i = 0; i < N; i++) { for (int j = M - 2; j >= 0; j--) { if (MAP[i][j] == '#') continue; int ny = j + 1; while (ny < M && MAP[i][ny] == '#') ny++; ny--; if (ny != j) { MAP[i][ny] = MAP[i][j]; MAP[i][j] = '#'; } } } } int BFS(int a, int b, char C, int Num) { queue<pair<int, int>> Q; Q.push(make_pair(a, b)); Visit[a][b] = Num; int Cnt = 1; while (Q.empty() == 0) { int x = Q.front().first; int y = Q.front().second; Q.pop(); for (int i = 0; i < 4; i++) { int nx = x + dx[i]; int ny = y + dy[i]; if (nx >= 0 && ny >= 0 && nx < N && ny < M) { if (Visit[nx][ny] == 0 && MAP[nx][ny] == C) { Visit[nx][ny] = Num; Q.push(make_pair(nx, ny)); Cnt++; } } } } return Cnt; } void Erase(int Num) { for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { if (Visit[i][j] == Num) MAP[i][j] = '#'; } } } void Down() { vector<int> V; memset(Visit, 0, sizeof(Visit)); int Num = 1; int Max_Block = 0; for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { if (MAP[i][j] == '#') continue; if (Visit[i][j] != 0) continue; int Result = BFS(i, j, MAP[i][j], Num); if (Result > Max_Block) { Max_Block = Result; V.clear(); V.push_back(Num); } else if (Result == Max_Block) { V.push_back(Num); } Num++; } } for (int i = 0; i < V.size(); i++) Erase(V[i]); } void Arrange_MAP() { for (int i = N - 2; i >= 0; i--) { for (int j = 0; j < M; j++) { if (MAP[i][j] == '#') continue; int nx = i + 1; while (nx < N && MAP[nx][j] == '#') nx++; nx--; if (nx != i) { MAP[nx][j] = MAP[i][j]; MAP[i][j] = '#'; } } } } void Solution() { int Idx = 0; for (int i = 0; i < Q; i++) { char Command = Cmd[i].first; if (Command == 'U') Up(Cmd[i].second); else if (Command == 'L') Left(); else if (Command == 'R') Right(); else Down(); Arrange_MAP(); } } void Solve() { int Tc; cin >> Tc; for (int T = 1; T <= Tc; T++) { Initialize(); Input(); Solution(); cout << "#" << T << endl; Print(); cout << endl; } } int main(void) { ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); //freopen("Input.txt", "r", stdin); Solve(); return 0; } | cs |
'[ SWEA Code ] > # SWEA - ' 카테고리의 다른 글
[ SWEA 7812 ] 옥히의 OK! 부동산 (C++) (0) | 2020.09.05 |
---|---|
[ SWEA 9843 ] 촛불 이벤트 (C++) (0) | 2020.09.01 |
[ SWEA 7730 ] 나무 깎는 홍준 (C++) (0) | 2020.08.27 |
[ SWEA 9015 ] 배열의 분할 (C++) (0) | 2020.08.25 |
[ SWEA 4534 ] 트리 흑백 색칠 (C++) (0) | 2020.08.22 |