HyeLog

백준_12100_2048 (Easy) 본문

알고리즘

백준_12100_2048 (Easy)

shj718 2022. 5. 26. 17:22

✨문제: https://www.acmicpc.net/problem/12100

 

12100번: 2048 (Easy)

첫째 줄에 보드의 크기 N (1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2

www.acmicpc.net

 

✨ 알고리즘: 브루트포스-비트마스크

(모든 방법의 수를 해보는 브루트포스 알고리즘의 구현 방식에는 1. 재귀 2. 브루트포스 두 가지가 있음. 재귀로도 풀이 가능 O)

 

✨ 풀이는 2단계로 이루어짐.

① 기울일 수 있는 방법 만들기 (Ex.     ←)

② 시뮬레이션 (그 방법대로 실제로 블록 이동시키기 - 문제 조건 고려해서)

 

✨ 비트마스크 핵심 아이디어: 상(↑)하(↓)좌(←)우(→)를 0,1,2,3 으로 표현. 이때 0,1,2,3은 4진수를 구성하는 숫자들임. 4진수는 2진수 2개로 나타낼 수 있음. 즉, 5번의 상하좌우 이동 방법(Ex. ↑ → ↓ ↓ ←)은 10개의 2진수를 5개의 4진수로 바꾼 것으로 표현 가능!

 

✨ 코드:

#include <iostream>
#include <vector>
#define LIMIT 5

using namespace std;

vector<int> makeDir(int k) { // 2진수를 4진수로 만들어서 방법의 수 만드는 함수.
	vector<int> dir(LIMIT);
	for (int i = 0; i < LIMIT; i++) {
		dir[i] = (k & 3);
		k >>= 2;
	}
	return dir;
}

int checkMax(vector<vector<int>> &a, vector<int> &dirs) { // 방법대로 블록 이동 후 가장 큰 블록값 리턴.
	int n = a.size();
	vector<vector<pair<int, bool>>> d(n, vector < pair<int, bool>>(n)); // pair<블록, 이번 이동에서 합쳐졌는지 여부>
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			d[i][j].first = a[i][j]; // 이동할 임시 배열 만들기.
		}
	}

	// 0:아래 1:위 2:왼쪽 3:오른쪽 방향대로 이동하기.
	for (int dir : dirs) {
		bool change = false; // 블록에 변화가 있었는지 나타내는 변수.
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				d[i][j].second = false; // 이번 이동에서 합쳐졌는지 여부 초기화.
			}
		}

		// 이동하기
		while (1) {
			change = false;
			if (dir == 0) { // 아래
				for (int i = n - 2; i >= 0; i--) { // 순서 중요! 0부터 시작하면 안됨!
					for (int j = 0; j < n; j++) {
						if (d[i][j].first == 0) continue; // 해당 칸에 수가 없는 경우
						// 해당 칸에 수가 있다면
						if (d[i + 1][j].first == 0) { // 아래칸에 수가 없다면 이동하기.
							d[i + 1][j].first = d[i][j].first;
							d[i + 1][j].second = d[i][j].second;
							d[i][j].first = 0;
							change = true;
						}
						else if (d[i + 1][j].first == d[i][j].first) { // 아래칸의 수가 현재칸의 수와 같아서 합쳐야하는 경우.
							if (d[i + 1][j].second == false && d[i][j].second == false) {
								d[i + 1][j].first *= 2;
								d[i + 1][j].second = true;
								d[i][j].first = 0;
								change = true;
							}
						}
						// 아래칸에 같지않은 수가 있다면 이동할수없음 = 아무일도 안일어남.
					}
				}
			}
			else if (dir == 1) { // 위
				for (int i = 1; i < n; i++) {
					for (int j = 0; j < n; j++) {
						if (d[i][j].first == 0) continue;
						if (d[i - 1][j].first == 0) { // 위칸에 수가 없다면 이동하기.
							d[i - 1][j].first = d[i][j].first;
							d[i - 1][j].second = d[i][j].second;
							d[i][j].first = 0;
							change = true;
						}
						else if (d[i - 1][j].first == d[i][j].first) { // 위칸의 수가 현재칸의 수와 같아서 합쳐야하는 경우.
							if (d[i - 1][j].second == false && d[i][j].second == false) {
								d[i - 1][j].first *= 2;
								d[i - 1][j].second = true;
								d[i][j].first = 0;
								change = true;
							}
						}
					}
				}
			}
			else if (dir == 2) { // 왼쪽
				for (int j = 1; j < n; j++) {
					for (int i = 0; i < n; i++) {
						if (d[i][j].first == 0) continue;
						if (d[i][j - 1].first == 0) { // 왼쪽칸에 수가 없다면 이동하기.
							d[i][j - 1].first = d[i][j].first;
							d[i][j - 1].second = d[i][j].second;
							d[i][j].first = 0;
							change = true;
						}
						else if (d[i][j - 1].first == d[i][j].first) { // 왼쪽칸의 수가 현재칸의 수와 같아서 합쳐야하는 경우.
							if (d[i][j - 1].second == false && d[i][j].second == false) {
								d[i][j - 1].first *= 2;
								d[i][j - 1].second = true;
								d[i][j].first = 0;
								change = true;
							}
						}
					}
				}
			}
			else if (dir == 3) { // 오른쪽
				for (int j = n - 2; j >= 0; j--) {
					for (int i = 0; i < n; i++) {
						if (d[i][j].first == 0) continue;
						if (d[i][j + 1].first == 0) { // 오른쪽칸에 수가 없다면 이동하기.
							d[i][j + 1].first = d[i][j].first;
							d[i][j + 1].second = d[i][j].second;
							d[i][j].first = 0;
							change = true;
						}
						else if (d[i][j + 1].first == d[i][j].first) { // 오른쪽칸의 수가 현재칸의 수와 같아서 합쳐야하는 경우.
							if (d[i][j + 1].second == false && d[i][j].second == false) {
								d[i][j + 1].first *= 2;
								d[i][j + 1].second = true;
								d[i][j].first = 0;
								change = true;
							}
						}
					}
				}
			}

			if (change == false) break; // 더 이상 블록에 변화가 없으면(이동할 수 없으면) 멈추기
		}
	}
	// 가장 큰 블록값 찾기
	int maxBlock = 0;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (maxBlock < d[i][j].first) {
				maxBlock = d[i][j].first;
			}
		}
	}
	return maxBlock;
}

int main() {
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n;
	cin >> n;
	vector<vector<int>> a(n, vector<int>(n));
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> a[i][j];
		}
	}

	int ans = 0;
	// 방법의 수 다 만들기
	for (int i = 0; i < (1 << (LIMIT * 2)); i++) {
		vector<int> dir = makeDir(i);
		// 방법대로 블록 이동하기
		int cur = checkMax(a, dir);
		if (ans < cur) ans = cur;
	}
	cout << ans << '\n';
	return 0;
}

 

'알고리즘' 카테고리의 다른 글

백준_1806_부분합  (0) 2022.05.26
백준_2003_수들의 합2  (0) 2022.05.26
백준_1062_가르침  (0) 2022.05.12
백준_14225_부분수열의 합  (0) 2022.05.11
백준_14889_스타트와 링크(브루트포스-순열)  (0) 2022.05.10