[java,백준12100]2048 Easy - 브루트포스(BFS, DFS)로 최대값 찾기
BFS나 DFS(+백트래킹)로 시뮬레이션을 활용하여 2048 게임을 최대 5번 진행했을 때 얻을 수 있는 가장 큰 블록 값을 구하는 문제입니다.
2048 이라는 게임을 이 링크에서 플레이해볼 수 있다.
헷갈리는 예시를 시각화 해보자
N=3
2 2 4 2 2 4 0 4 4
0 0 0 -> 0 0 0 -> 0 0 0
0 0 0 0 0 0 0 0 0
(초기) (오른쪽) (오른쪽)
즉, 오른쪽 때 0 0 8이 된다고 헷갈리지 말자.
2 2 4 4 0 4 4 4 0
0 0 0 -> 0 0 0 -> 0 0 0
0 0 0 0 0 0 0 0 0
(초기) (왼쪽) (왼쪽)
즉, 왼쪽 때 8 0 0이 된다고 헷갈리지 말자.
풀이
구현 시 주의사항
💡 DFS로 4방향 이동을 중복순열처럼 탐색+상태기록(백트래킹)+각 방향으로 이동 시뮬레이션방식으로 구현한다.
=> 움직일 때 마다 디버깅 추천!! (배열 출력해보아라)
💡 BFS는 상태기록(백트래킹) 없이 큐에 기록하는 차이가 전부이다.
💡 최대 5번이 전부이므로 복잡도는 신경 쓸 필요 없다.
핵심 구현 사항
- DFS나 BFS를 통한 최대 5번의 이동 탐색!
-
제일 중요한 구현사항
- 이동 방향의 블록 우선순위 처리
- 블록 합치기 처리 (이미 합친건 합치지 않기)
- 각 상태에서 최대값 갱신!
블록 이동 처리(방향,좌표 설정)
static void move(int dir, Pair[][] board) {
if (dir == 0) { // '->'
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
moveElement(i, N - 1 - j, board, dir);
}
}
}else if (dir == 1) { // '아래'
// ...
}
블록 이동 처리(while)
-
nx<0 || ny<0 || nx>N-1 || ny>N-1
: 범위 초과 일 때 처리 -> 끝 -
board[nx][ny].val == 0)
: 빈 공간 일 때 처리 -> 블록 이동하려고 -
board[nx][ny].val == board[x][y].val && !board[nx][ny].sum
: 숫자가 같은 블록+이미 합친 적 있는 블록 일 때 처리 -> 블록 합치려고 -
board[nxt] != board[nxt-1]
: 숫자가 다른 블록 일 때 처리 -> 끝
static void moveElement(int x, int y, Pair[][] board, int dir) {
int nx = x, ny = y;
while (true) {
nx += dx[dir];
ny += dy[dir];
if (nx < 0 || ny < 0 || nx >= N || ny >= N) break;
// 이동 및 합치기 처리
// ...
x=nx;
y=ny; //이전 기록
}
}
전체 코드 - BFS, DFS 둘 다
DFS 방식1
이미 합친 블록인지 체크: Pair 사용한 풀이
public class BOJ_2048Easy_12100 {
static int N;
static Pair[][] map;
static int result;
static int[] dx = {0, 1, 0, -1};
static int[] dy = {1, 0, -1, 0};
//방향 끝가지 이동 함수(while)
static void moveElement(int x, int y, Pair[][] board, int dir) {
int nx = x;
int ny = y;
while (true) {
nx = dx[dir]+nx;
ny = dy[dir]+ny;
if(nx<0 || ny<0 || nx>N-1 || ny>N-1) break;
if (board[nx][ny].val == 0) {
board[nx][ny].val=board[x][y].val;
board[nx][ny].sum=board[x][y].sum;
board[x][y].val=0; board[x][y].sum=false;
} else if (board[nx][ny].val == board[x][y].val && !board[nx][ny].sum) {
board[nx][ny].val += board[x][y].val;
board[nx][ny].sum = true;
board[x][y].val=0; board[x][y].sum=false;
break;
} else { //board[nxt] != board[nxt-1]
break;
}
x=nx;
y=ny; //이전 기록
}
}
//방향 끝까지 이동 함수(좌표설정)
static void move(int dir, Pair[][] board) {
if (dir == 0) { // '->'
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
moveElement(i, N - 1 - j, board, dir);
}
}
}else if (dir == 1) { // '아래'
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
moveElement(N - 1 - j, i, board, dir);
}
}
}else if (dir == 0) { // '<-'
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
moveElement(i, j, board, dir);
}
}
}else { // '위'
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
moveElement(j, i, board, dir);
}
}
}
}
static void dfs(int depth, Pair[][] board) {
//base condition
if (depth == 5) {
int maxVal=0; //제일 큰 값
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
maxVal = Math.max(maxVal, board[i][j].val);
}
}
result = Math.max(result, maxVal);
return;
}
//recursion
for (int i = 0; i < 4; i++) {
Pair[][] copy = new Pair[N + 1][N + 1]; //항상 복제본을 넘겨야 그 당시의 상태값을 유지할 수 있다.
for (int j = 0; j < N; j++) {
for (int k = 0; k < N; k++) {
copy[j][k]=new Pair(board[j][k].val, false); //false 로 init
}
} //간단한 복제면 clone 메소드 사용 추천
move(i, copy); //게임시작
// printAll(depth+1, copy, i); //debug
dfs(depth + 1, copy);
}
}
private static void printAll(int depth, Pair[][] copy, int dir) {
System.out.println("depth:"+ depth);
System.out.println("dir:"+ dir);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(copy[i][j]+",");
}
System.out.println();
}
System.out.println();
}
public static void main(String[] args) throws IOException {
//input
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
N = Integer.parseInt(br.readLine());
map = new Pair[N+1][N+1];
for (int i = 0; i < N; i++) {
StringTokenizer stk = new StringTokenizer(br.readLine(), " ");
for (int j = 0; j < N; j++) {
map[i][j] = new Pair(Integer.parseInt(stk.nextToken()), false);
}
}
//run
dfs(0, map);
//output
System.out.println(result);
}
static class Pair {
int val; boolean sum;
public Pair(int val, boolean sum) {
this.val=val; this.sum=sum;
}
@Override
public String toString() {
return "Pair{" +
"val=" + val +
", sum=" + sum +
'}';
}
}
}
/*
3
2 2 4
0 0 0
0 0 0
3
2 0 0
2 2 0
2 0 0
4
2 4 8 2
2 4 0 0
2 0 0 0
2 0 2 0
*/
DFS 방식2
이미 합친 블록인지 체크: flagMap[][] 사용한 풀이
import java.io.*;
import java.util.*;
public class 이공사팔_12100 {
static final int MAX_COUNT = 5;
static final int[] dx = {0, 1, 0, -1};
static final int[] dy = {1, 0, -1, 0};
static int N, result;
static int[][] inArr;
public static void moveElement(int x, int y, int dir, int[][] flagMap) {
int nx = x; int ny = y;
int init = inArr[x][y]; //초기값
while (true) {
nx += dx[dir];
ny += dy[dir];
//base condition -> 못 움직일 때 (범위 초과, 숫자 다른 블록, 이미 합친 블록)
if(nx<0 || ny<0 || nx>=N || ny>=N) break;
if(inArr[nx][ny] != 0 && init != inArr[nx][ny]) break;
if(inArr[nx][ny] != 0 && init == inArr[nx][ny]
&& (flagMap[nx][ny] == 1 || flagMap[x][y] == 1)) break; //flag는 이동하는 블록과 이동할 위치의 블록 둘다 체크해야 한다.
//while - update
if (inArr[nx][ny] == 0) {
inArr[nx][ny] = init;
} else {
//여기는 숫자 합치는 분기밖에 없음 -> 나머지 분기는 위에서 이미 처리
inArr[nx][ny] = init*2;
init = init*2; //init 도 update
flagMap[nx][ny] = 1;
}
inArr[x][y] = 0;
x = nx; y = ny;
}
}
public static void move(int dir, int debug) {
// 구현 - N*N 각 원소마다 적용 + 방향별 구분(방향쪽 원소부터 적용) + 이동은 끝까지(요소단위 while)
int[][] flagMap = new int[N + 1][N + 1]; //N 작으니 매번 재할당 (초기값 0)
for (int i = 0; i < N; i++) {
if (dir == 0) { //오른쪽 -> 방향쪽 원소부터 적용 중요
for (int j = N - 1; j >= 0; j--) {
moveElement(i, j, dir, flagMap); // 끝까지 이동
}
} else if (dir == 1) { //아래쪽
for (int j = N - 1; j >= 0; j--) {
moveElement(j, i, dir, flagMap);
}
} else if (dir == 2) { //왼쪽
for (int j = 0; j < N; j++) {
moveElement(i, j, dir, flagMap);
}
} else { //dir == 4 (위쪽)
for (int j = 0; j < N; j++) {
moveElement(j, i, dir, flagMap);
}
}
}
//debug
// System.out.println("방향:"+dir+" 깊이:"+debug);
// for (int i = 0; i < N; i++) {
// System.out.println(Arrays.toString(inArr[i]));
// }
}
public static void dfs(int depth) {
//base condition
if (depth == MAX_COUNT) {
int val = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
val = Math.max(val, inArr[i][j]);
}
}
result = Math.max(val, result);
return;
}
//recursion
int copy[][] = new int[N + 1][N + 1]; // N 작으니 매번 재할당
for(int i=0; i<N; i++) copy[i] = inArr[i].clone(); // 2차원 배열 복제
for (int i = 0; i < 4; i++) {
int dir = i; //방향
move(dir, depth);
dfs(depth + 1);
for(int k=0; k<N; k++) inArr[k] = copy[k].clone(); //backtracking
}
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
N = Integer.parseInt(br.readLine());
inArr = new int[N+1][N+1];
//input
for (int i = 0; i < N; i++) {
StringTokenizer stk = new StringTokenizer(br.readLine(), " ");
for (int j = 0; j < N; j++) {
inArr[i][j] = Integer.parseInt(stk.nextToken());
}
}
//debug
// System.out.println("입력 값");
// for (int i = 0; i < N; i++) {
// System.out.println(Arrays.toString(inArr[i]));
// }
//run
dfs(0);
//output
System.out.println(result);
}
}
BFS 방식
이미 합친 블록인지 체크: Pair 사용한 풀이
public class BOJ_2048Easy_12100 {
static int N;
static Pair[][] map;
static int result;
static int[] dx = {0, 1, 0, -1};
static int[] dy = {1, 0, -1, 0};
//방향 끝가지 이동 함수(while)
static void moveElement(int x, int y, Pair[][] board, int dir) {
int nx = x;
int ny = y;
while (true) {
nx = dx[dir]+nx;
ny = dy[dir]+ny;
if(nx<0 || ny<0 || nx>N-1 || ny>N-1) break;
if (board[nx][ny].val == 0) {
board[nx][ny].val=board[x][y].val;
board[nx][ny].sum=board[x][y].sum;
board[x][y].val=0; board[x][y].sum=false;
} else if (board[nx][ny].val == board[x][y].val && !board[nx][ny].sum) {
board[nx][ny].val += board[x][y].val;
board[nx][ny].sum = true;
board[x][y].val=0; board[x][y].sum=false;
break;
} else { //board[nxt] != board[nxt-1]
break;
}
x=nx;
y=ny; //이전 기록
}
}
//방향 끝까지 이동 함수(좌표설정)
static void move(int dir, Pair[][] board) {
if (dir == 0) { // '->'
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
moveElement(i, N - 1 - j, board, dir);
}
}
}else if (dir == 1) { // '아래'
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
moveElement(N - 1 - j, i, board, dir);
}
}
}else if (dir == 0) { // '<-'
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
moveElement(i, j, board, dir);
}
}
}else { // '위'
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
moveElement(j, i, board, dir);
}
}
}
}
static void bfs(Pair[][] board) {
Queue<Pair[][]> qu = new ArrayDeque<>();
qu.offer(board);
int level=0;
while (!qu.isEmpty()) {
int size = qu.size();
level++;
for (int s = 0; s < size; s++) {
Pair[][] cur = qu.poll();
for (int i = 0; i < 4; i++) {
Pair[][] copy = new Pair[N + 1][N + 1]; //항상 복제본을 넘겨서 큐에 문제없이 담는다.
for (int j = 0; j < N; j++) {
for (int k = 0; k < N; k++) {
copy[j][k]=new Pair(cur[j][k].val, false); //false 로 init
}
} //간단한 복제면 clone 메소드 사용 추천
move(i, copy);
// printAll(level, copy, i); //debug
qu.offer(copy);
}
}
//base condition
if (level == 5) {
while (!qu.isEmpty()) {
int maxVal=0; //제일 큰 값
Pair[][] cur = qu.poll();
for (int j = 0; j < N; j++) {
for (int k = 0; k < N; k++) {
maxVal = Math.max(maxVal, cur[j][k].val);
}
}
result = Math.max(result, maxVal);
}
break;
}
}
}
private static void printAll(int depth, Pair[][] copy, int dir) {
System.out.println("depth:"+ depth);
System.out.println("dir:"+ dir);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(copy[i][j]+",");
}
System.out.println();
}
System.out.println();
}
public static void main(String[] args) throws IOException {
//input
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
N = Integer.parseInt(br.readLine());
map = new Pair[N+1][N+1];
for (int i = 0; i < N; i++) {
StringTokenizer stk = new StringTokenizer(br.readLine(), " ");
for (int j = 0; j < N; j++) {
map[i][j] = new Pair(Integer.parseInt(stk.nextToken()), false);
}
}
//run
bfs(map);
//output
System.out.println(result);
}
static class Pair {
int val; boolean sum;
public Pair(int val, boolean sum) {
this.val=val; this.sum=sum;
}
@Override
public String toString() {
return "Pair{" +
"val=" + val +
", sum=" + sum +
'}';
}
}
}
/*
3
2 2 4
0 0 0
0 0 0
3
2 0 0
2 2 0
2 0 0
4
2 4 8 2
2 4 0 0
2 0 0 0
2 0 2 0
*/
주관적인 생각의 풀이이므로 틀린 부분 지적은 언제나 환영합니다⚡
댓글남기기