https://www.acmicpc.net/problem/12100
12100번: 2048 (Easy)
첫째 줄에 보드의 크기 N (1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2
www.acmicpc.net
코드설명
시뮬레이션(Simulation) + 브루트포스(brute force) 문제입니다.
문제에서 주어진대로 모든 경우를 완전탐색하면 됩니다.
문제에서 숫자들을 2048 게임의 원리대로 합치는것은 문제에 주어진대로 구현하면 됩니다.
처음에 4가지 방향을 모두 따로 구현하면서 코드를 줄일 수 있는 방법이 있을까 계속 고민하면서 풀었지만 다른 풀이가 떠오르지 않았습니다.
다만, 구현중에서 가장 문제가 있었던 부분은, Map, 즉 2048 의 숫자를 기록해두는 배열을 처음에 전역배열로 설정해두었기에 storeMap값이 다음 simulate 재귀함수가 실행될경우 바뀐값으로 갱신되어 올바르게 완전탐색을 진행하지 못했었습니다.
전역배열말고, Node[][] storeMap을 각 simulate함수마다 지역변수로 설정하여 해당 문제를 해결할 수 있습니다.
이런 tempMap은 기본적으로 함수 내에서 하도록 습관을 들여야할 것 같습니다.
전역변수에 둘경우 관리가 어렵고, 모든 함수에서 똑같이 접근하므로 각 함수에서의 배열을 따로 선언하지 못하게됩니다.
// public static Node[][] storeMap;
// storeMap = new Node[N][N];
public static void simulate(int level) {
...
...
Node[][] storeMap = new Node[N][N];
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
storeMap[i][j] = new Node(map[i][j].num, false);
}
}
...
...
}
위와 같은 오류를 찾아낸 방법은, 아래의 예시와 과정입니다.
3
16 2 2
2 2 2
2 2 16
위의 예시입니다.
32가 나오는 과정을 표현해보면,
===0====
16 4 4
4 2 16
0 0 0
===1====
0 16 8
4 2 16
0 0 0
===2====
4 16 8
0 2 16
0 0 0
===3====
4 16 8
2 16 0
0 0 0
===4====
4 32 8
2 0 0
0 0 0
답 : 32
코드
package Main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
public static int N,M, K;
public static Node[][] map;
// public static Node[][] storeMap;
public static int answer = 0;
public static int[] dx = {-1, 1, 0, 0};
public static int[] dy = { 0, 0, -1, 1};
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
map = new Node[N][N];
// storeMap = new Node[N][N];
for(int i=0;i<N;i++) {
st = new StringTokenizer(br.readLine());
for(int j=0;j<N;j++) {
int a = Integer.parseInt(st.nextToken());
map[i][j] = new Node(a, false);
}
}
// mergeMap(0);
// print(0);
// mergeMap(3);
// print(1);
// mergeMap(0);
// print(2);
// mergeMap(2);
// print(3);
// mergeMap(0);
// print(4);
simulate(0);
System.out.println(answer);
}
public static void simulate(int level) {
//가장 큰 블록값은 매번 검사해도 되지만, 결국 많이 이동한결과가 최대값이므로 매번 하지 않아서 효율성을 개선시키자.
if(level == 5) {
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
answer = Math.max(answer, map[i][j].num);
}
}
return ;
}
Node[][] storeMap = new Node[N][N];
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
storeMap[i][j] = new Node(map[i][j].num, false);
}
}
for(int dir = 0; dir < 4; dir++) {
//각 방향으로 이동시키자.
//이떄 중요한점은, 한번 이동시키고 난 이후에 merged를 모두 fasel로 초기화 시키고서 작동시켜야한다는것이다.
mergeMap(dir);
simulate(level + 1);
//원상복구.
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
map[i][j].num = storeMap[i][j].num;
map[i][j].merged = false;
}
}
}
}
public static void mergeMap(int dir) {
//시작할떄는 합병여부를 false로 선언하고 시작합니다.
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
map[i][j].merged = false;
}
}
//어떤 방향으로 합쳐질떄.
//위쪽으로 합쳐진다고 가정해보자.
//먼저 해당 부분에 Block이 존재한다면, 이동시킬 방향과 가까운곳부터 블록을 이동시킬 것 이다.
//만약 이동시킬 방향에서 BLock을 못찾거나 이미 해당 블록이 merged 된 상태라면 바로 아래 블록에 넣어준다.
if(dir == 0) {
//블록을 위로 이동시키는 경우.
//N가지의 열을 기준으로 모두 실행됩니다.
for(int col = 0; col < N; col ++) {
//해당 행에서 시작점을 잡아줍니다.
for(int row = 1; row < N; row++) {
int startNum = map[row][col].num;
int nr = row;
//row가 시작점이 되고 그 시작점을 기준으로 While문을 통해서 가장 맨위의 값을 찾아나갑니다.
while(nr >= 0) {
nr = nr + dx[dir];
//만약 범위를 벗어난다면, 가장 맨위칸이 이동할 칸이 될 것 입니다..
if(nr <= -1) {
map[row][col].num = 0;
map[row][col].merged = false;
map[0][col].num = startNum;
map[0][col].merged = false;
break;
}
//만약 해당 칸이 비어있다면, 한칸 올라갑니다.
//다른 작업은 굳이 진행하지 않아도 됩니다.
else if(map[nr][col].num == 0) {
}
//만약 해당 칸이 현재 숫자와 같고, 아직 합쳐진적도 없다면, 해당 부분에서 합쳐주고 종료할 것 입니다.
else if(map[nr][col].num == map[row][col].num && map[nr][col].merged == false) {
map[row][col].num = 0;
map[row][col].merged = false;
map[nr][col].num *= 2;
map[nr][col].merged = true;
break;
}
//만약 해당 칸이 현재 숫자와 같은데, 이미 합쳐져있다면, 한칸 내려야합니다.
else if(map[nr][col].num == map[row][col].num && map[nr][col].merged == true) {
nr -= dx[dir];
map[row][col].num = 0;
map[row][col].merged = false;
map[nr][col].num = startNum;
map[nr][col].merged = false;
break;
}
//만약 서로 다른 숫자라면,
else if(map[nr][col].num != map[row][col].num) {
nr -= dx[dir];
map[row][col].num = 0;
map[row][col].merged = false;
map[nr][col].num = startNum;
map[nr][col].merged = false;
break;
}
}
}
}
}
//하
else if(dir == 1) {
//블록을 위로 이동시키는 경우.
//N가지의 열을 기준으로 모두 실행됩니다.
for(int col = 0; col < N; col ++) {
int startRow = 0;
//해당 행에서 시작점을 잡아줍니다.
for(int row = N-2; row >= 0; row--) {
startRow = row;
int startNum = map[startRow][col].num;
int nr = row;
//row가 시작점이 되고 그 시작점을 기준으로 While문을 통해서 가장 맨위의 값을 찾아나갑니다.
while(nr <= N) {
nr = nr + dx[dir];
//만약 범위를 벗어난다면, 가장 맨위칸이 이동할 칸이 될 것 입니다..
if(nr >= N) {
map[startRow][col].num = 0;
map[startRow][col].merged = false;
map[N-1][col].num = startNum;
map[N-1][col].merged = false;
break;
}
//만약 해당 칸이 비어있다면, 한칸 올라갑니다.
//다른 작업은 굳이 진행하지 않아도 됩니다.
else if(map[nr][col].num == 0) {
}
//만약 해당 칸이 현재 숫자와 같고, 아직 합쳐진적도 없다면, 해당 부분에서 합쳐주고 종료할 것 입니다.
else if(map[nr][col].num == map[startRow][col].num && map[nr][col].merged == false) {
map[startRow][col].num = 0;
map[startRow][col].merged = false;
map[nr][col].num *= 2;
map[nr][col].merged = true;
break;
}
//만약 해당 칸이 현재 숫자와 같은데, 이미 합쳐져있다면, 한칸 내려야합니다.
else if(map[nr][col].num == map[startRow][col].num && map[nr][col].merged == true) {
nr -= dx[dir];
map[startRow][col].num = 0;
map[startRow][col].merged = false;
map[nr][col].num = startNum;
map[nr][col].merged = false;
break;
}
//만약 서로 다른 숫자라면,
else if(map[nr][col].num != map[startRow][col].num) {
nr -= dx[dir];
map[startRow][col].num = 0;
map[startRow][col].merged = false;
map[nr][col].num = startNum;
map[nr][col].merged = false;
break;
}
}
}
}
}
//좌 : dir : 2
else if(dir == 2) {
//블록을 위로 이동시키는 경우.
//N가지의 열을 기준으로 모두 실행됩니다.
for(int row = 0; row < N; row ++) {
//해당 행에서 시작점을 잡아줍니다.
for(int col = 1; col < N; col++) {
int startNum = map[row][col].num;
int nc = col;
//row가 시작점이 되고 그 시작점을 기준으로 While문을 통해서 가장 맨위의 값을 찾아나갑니다.
while(nc >= 0) {
nc = nc + dy[dir];
//만약 범위를 벗어난다면, 가장 맨위칸이 이동할 칸이 될 것 입니다..
if(nc == -1) {
map[row][col].num = 0;
map[row][col].merged = false;
map[row][0].num = startNum;
map[row][0].merged = false;
break;
}
//만약 해당 칸이 비어있다면, 한칸 올라갑니다.
//다른 작업은 굳이 진행하지 않아도 됩니다.
else if(map[row][nc].num == 0) {
}
//만약 해당 칸이 현재 숫자와 같고, 아직 합쳐진적도 없다면, 해당 부분에서 합쳐주고 종료할 것 입니다.
else if(map[row][nc].num == map[row][col].num && map[row][nc].merged == false) {
map[row][col].num = 0;
map[row][col].merged = false;
map[row][nc].num *= 2;
map[row][nc].merged = true;
break;
}
//만약 해당 칸이 현재 숫자와 같은데, 이미 합쳐져있다면, 한칸 내려야합니다.
else if(map[row][nc].num == map[row][col].num && map[row][nc].merged == true) {
nc -= dy[dir];
map[row][col].num = 0;
map[row][col].merged = false;
map[row][nc].num = startNum;
map[row][nc].merged = false;
break;
}
//만약 서로 다른 숫자라면,
else if(map[row][nc].num != map[row][col].num) {
nc -= dy[dir];
map[row][col].num = 0;
map[row][col].merged = false;
map[row][nc].num = startNum;
map[row][nc].merged = false;
break;
}
}
}
}
}
//우 : dir : 3
else if(dir == 3) {
//블록을 위로 이동시키는 경우.
//N가지의 열을 기준으로 모두 실행됩니다.
for(int row = 0; row < N; row ++) {
//해당 행에서 시작점을 잡아줍니다.
for(int col = N-2; col >= 0; col--) {
int startNum = map[row][col].num;
int nc = col;
//row가 시작점이 되고 그 시작점을 기준으로 While문을 통해서 가장 맨위의 값을 찾아나갑니다.
while(nc <= N) {
nc = nc + dy[dir];
//만약 범위를 벗어난다면, 가장 맨위칸이 이동할 칸이 될 것 입니다..
if(nc == N) {
map[row][col].num = 0;
map[row][col].merged = false;
map[row][N-1].num = startNum;
map[row][N-1].merged = false;
break;
}
//만약 해당 칸이 비어있다면, 한칸 올라갑니다.
//다른 작업은 굳이 진행하지 않아도 됩니다.
else if(map[row][nc].num == 0) {
}
//만약 해당 칸이 현재 숫자와 같고, 아직 합쳐진적도 없다면, 해당 부분에서 합쳐주고 종료할 것 입니다.
else if(map[row][nc].num == map[row][col].num && map[row][nc].merged == false) {
map[row][col].num = 0;
map[row][col].merged = false;
map[row][nc].num *= 2;
map[row][nc].merged = true;
break;
}
//만약 해당 칸이 현재 숫자와 같은데, 이미 합쳐져있다면, 한칸 내려야합니다.
else if(map[row][nc].num == map[row][col].num && map[row][nc].merged == true) {
nc -= dy[dir];
map[row][col].num = 0;
map[row][col].merged = false;
map[row][nc].num = startNum;
map[row][nc].merged = false;
break;
}
//만약 서로 다른 숫자라면,
else if(map[row][nc].num != map[row][col].num) {
nc -= dy[dir];
map[row][col].num = 0;
map[row][col].merged = false;
map[row][nc].num = startNum;
map[row][nc].merged = false;
break;
}
}
}
}
}
}
public static void print(int level) {
System.out.println("==="+level+"====");
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
System.out.print(" "+map[i][j].num);
}
System.out.println();
}
}
}
class Node{
int num;
boolean merged = false;
public Node(int num, boolean merged) {
this.num = num;
this.merged = merged;
}
}
'알고리즘 > 백준' 카테고리의 다른 글
[백준] 9663 N-Queen - 브루트포스(BruteForce) + 시간초과(TimeOut) JAVA (0) | 2022.07.11 |
---|---|
[백준] 14502 연구소 - 브루트포스(BruteForce) + BFS(너비우선탐색) + 조합(Combination) JAVA (0) | 2022.03.05 |
[백준] 13305번 주유소 - 그리디(Greedy, 탐욕법) JAVA (0) | 2021.12.23 |
[백준] 1541번 잃어버린 괄호 - 문자열 파싱(Parsing) + Stack(스택) + 그리디(탐욕법, Greedy) JAVA (0) | 2021.12.22 |
[백준] 11399번 - ATM (0) | 2021.12.20 |