엄청 어렵지는 않았지만 조건을 잘못 줘서 디버깅을 좀 하느라 시간을 많이 썼다. 문제 자체가 BFS + 각 가능한 노드 계산(꽤 양이 된다.) 문제여서 시간이 초과될까봐 조마조마했다. 다행히 시간초과는 나지 않았다.
예전에 프로그래밍 연습이라는 과목에서 2048 게임 만들기를 파이널 프로젝트로 했었는데 그때 생각도 나고 좋았다. 시간이 참 빠른 것 같다. 그 때가 불과 2년 반 전인데, 그땐 c++도 모르고 BFS도 모르는 비전공자 인문대생이였던 내가 벌써 컴퓨터공학 전공자로 곧 졸업을 하게 된다.
참고로 나는 이 문제에 2시간 20분을 썼다. 디버깅 때문이다. 한번 풀 때 제대로 풀어야 한다. 다행히 다음번에 푼 문제를 40분만에 후다닥 풀어서 통과했다. 실전이 아닌데도 정말 심장 쫄리는 경험이었다.
문제
2048 게임은 4×4 크기의 보드에서 혼자 즐기는 재미있는 게임이다. 이 링크를 누르면 게임을 해볼 수 있다.
이 게임에서 한 번의 이동은 보드 위에 있는 전체 블록을 상하좌우 네 방향 중 하나로 이동시키는 것이다. 이때, 같은 값을 갖는 두 블록이 충돌하면 두 블록은 하나로 합쳐지게 된다. 한 번의 이동에서 이미 합쳐진 블록은 또 다른 블록과 다시 합쳐질 수 없다. (실제 게임에서는 이동을 한 번 할 때마다 블록이 추가되지만, 이 문제에서 블록이 추가되는 경우는 없다)
이 문제에서 다루는 2048 게임은 보드의 크기가 N×N 이다. 보드의 크기와 보드판의 블록 상태가 주어졌을 때, 최대 5번 이동해서 만들 수 있는 가장 큰 블록의 값을 구하는 프로그램을 작성하시오.
https://www.acmicpc.net/problem/12100
12100번: 2048 (Easy)
첫째 줄에 보드의 크기 N (1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2
www.acmicpc.net
구상
(메모에 썼던 내용이다. 설명이 빈약할 수 있음)
1. BFS. 탐색 종료조건은 깊이 5 초과, 움직임 없음. 종료시 max 숫자 저장
2. 큐 설정, 현재 보드 상태와 움직인 횟수 저장
3. 큐의 front에 대해 각 이동방향으로 스와이프했을 때 결과가 종료조건이 아니면 큐에 저장, 종료조건이면 max저장
4. 큐가 비면 max 리턴
이렇게까지만 쓰고 내가 간과한 것이 한번의 이동에 한번만 합쳐질 수 있다는 조건이었다.
[주의사항]
1. 한번 합쳐진 블록은 합쳐졌다는 사실을 저장해서 다른 블록과 합쳐지지 못하게 해야 한다.
위 조건까지 고려하면 이동 함수는 이렇게 짜면 된다.
1. 블럭들의 움직임이 있는 동안,
2. 이동 방향에 0이 있는 경우 0 자리를 대체한다. 이 때, 합쳐졌는지 정보도 블록과 함께 이동한다.
3. 이동 방향에 같은 숫자를 가졌으면서 합쳐진 이력이 없는 블럭이 있는 경우(당연히 지금 보고 있는 내 블럭도 합쳐진 이력이 없어야 한다.) 그 블럭의 숫자를 *2하고, 내 블럭은 0으로 없앤다. 이 때, *2한 블럭은 합쳐졌다고 표시한다.
4. 2, 3의 경우가 아니면 그대로 둔다.
5. 블럭 움직임이 있는 동안 2-4를 반복한다.
그렇게 짠 코드가 아래와 같다. 난 정말 말로 풀어 설명하는걸 잘 못하는 것 같다. 이해가 안되는 부분이 있으면 꼭 댓글에 남겨줬으면 좋겠다. 누가 보고 있는지는 모르겠지만...
#include<iostream>
#include<queue>
#include<climits>
using namespace std;
int find_max(int** board, int n) {
int max = -1;
for(int i=0; i<n; i++) {
for(int j=0; j<n; j++) {
if(board[i][j] > max) {
max = board[i][j];
}
}
}
return max;
}
int** move(int** board, int n, int direction) {
int** result;
int** combined;
result = new int*[n];
combined = new int*[n];
bool changed = false;
for(int i=0; i<n; i++) {
result[i] = new int[n];
combined[i] = new int[n];
for(int j=0; j<n; j++) {
result[i][j] = board[i][j];
combined[i][j] = 0;
}
}
if(direction == 0) {
//up
bool moved = true;
while(moved==true) {
moved = false;
for(int i=1; i<n; i++) {
for(int j=0; j<n; j++) {
if(result[i][j] != 0 && result[i-1][j] == 0) {
result[i-1][j] = result[i][j];
combined[i-1][j] = combined[i][j];
combined[i][j] = 0;
result[i][j] = 0;
moved = true;
}
else if(result[i][j] != 0 && combined[i][j] == 0 && result[i-1][j] == result[i][j] && combined[i-1][j] == 0) {
result[i-1][j] = 2*result[i][j];
result[i][j] = 0;
combined[i-1][j] = 1;
moved = true;
}
else {
result[i][j] = result[i][j];
}
}
}
}
}
else if (direction == 1) {
//down
bool moved = true;
while(moved==true) {
moved = false;
for(int i=n-2; i>=0; i--) {
for(int j=0; j<n; j++) {
if(result[i][j] != 0 && result[i+1][j] == 0) {
result[i+1][j] = result[i][j];
combined[i+1][j] = combined[i][j];
combined[i][j] = 0;
result[i][j] = 0;
moved = true;
}
else if(result[i][j] != 0 && combined[i][j] == 0 && result[i+1][j] == result[i][j] && combined[i+1][j] == 0) {
result[i+1][j] = 2*result[i][j];
result[i][j] = 0;
combined[i+1][j] = 1;
moved = true;
}
else {
result[i][j] = result[i][j];
}
}
}
}
}
else if (direction == 2) {
//right
bool moved = true;
while(moved==true) {
moved = false;
for(int i=0; i<n; i++) {
for(int j=n-2; j>=0; j--) {
if(result[i][j] != 0 && result[i][j+1] == 0) {
result[i][j+1] = result[i][j];
combined[i][j+1] = combined[i][j];
combined[i][j] = 0;
result[i][j] = 0;
moved = true;
}
else if(result[i][j] != 0 && combined[i][j] == 0 && result[i][j+1] == result[i][j] && combined[i][j+1] == 0) {
result[i][j+1] = 2*result[i][j];
result[i][j] = 0;
combined[i][j+1] = 1;
moved = true;
}
else {
result[i][j] = result[i][j];
}
}
}
}
}
else {
//left
bool moved = true;
while(moved==true) {
moved = false;
for(int i=0; i<n; i++) {
for(int j=1; j<n; j++) {
if(result[i][j] != 0 && result[i][j-1] == 0) {
result[i][j-1] = result[i][j];
combined[i][j-1] = combined[i][j];
combined[i][j] = 0;
result[i][j] = 0;
moved = true;
}
else if(result[i][j] != 0 && combined[i][j] == 0 && result[i][j-1] == result[i][j] && combined[i][j-1] == 0) {
result[i][j-1] = 2*result[i][j];
result[i][j] = 0;
combined[i][j-1] = 1;
moved = true;
}
else {
result[i][j] = result[i][j];
}
}
}
}
}
return result;
}
int main(int argc, char** argv)
{
ios_base :: sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n;
cin >> n;
int** first;
first = new int*[n];
for(int i=0; i<n; i++) {
first[i] = new int[n];
for(int j=0; j<n; j++) {
cin >> first[i][j];
}
}
queue<pair<int**, int> > queue;
queue.push(pair<int**, int>(first, 0));
int max = -1;
while(!queue.empty()) {
pair<int**, int> now = queue.front();
queue.pop();
if(now.second == 5) {
int now_max = find_max(now.first, n);
if(now_max > max) {
max = now_max;
}
continue;
}
for(int i=0; i<4; i++) {
int** moved = move(now.first, n, i);
queue.push(pair<int**, int>(moved, now.second + 1));
}
}
cout << max << "\n";
return 0;
}
'PS문제들' 카테고리의 다른 글
[백준] 14500: 테트로미노 (with C++) (0) | 2022.08.23 |
---|---|
[백준] 14499: 주사위 굴리기(with C++) (0) | 2022.08.22 |
[백준] 13460: 구슬 탐험 (with C++) (0) | 2022.08.21 |
[프로그래머스] lv4. 호텔 방 배정 (with Swift) (0) | 2022.08.19 |
[프로그래머스] lv3. 경주로 건설(with Swift) (0) | 2022.08.19 |
댓글