문제
2048 게임은 4×4 크기의 보드에서 혼자 즐기는 재미있는 게임이다. 이 링크를 누르면 게임을 해볼 수 있다.
이 게임에서 한 번의 이동은 보드 위에 있는 전체 블록을 상하좌우 네 방향 중 하나로 이동시키는 것이다. 이때, 같은 값을 갖는 두 블록이 충돌하면 두 블록은 하나로 합쳐지게 된다. 한 번의 이동에서 이미 합쳐진 블록은 또 다른 블록과 다시 합쳐질 수 없다. (실제 게임에서는 이동을 한 번 할 때마다 블록이 추가되지만, 이 문제에서 블록이 추가되는 경우는 없다)
이 문제에서 다루는 2048 게임은 보드의 크기가 N×N 이다. 보드의 크기와 보드판의 블록 상태가 주어졌을 때, 최대 5번 이동해서 만들 수 있는 가장 큰 블록의 값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 보드의 크기 N (1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 게임판의 초기 상태가 주어진다. 0은 빈 칸을 나타내며, 이외의 값은 모두 블록을 나타낸다. 블록에 쓰여 있는 수는 2보다 크거나 같고, 1024보다 작거나 같은 2의 제곱꼴이다. 블록은 적어도 하나 주어진다.
출력
최대 5번 이동시켜서 얻을 수 있는 가장 큰 블록을 출력한다.
풀이
기본적으로 2048의 상하좌우 이동에 대한 메서드는 아래 글을 참고하면 된다
https://developerjisu.tistory.com/120
[SWEA] 6109번 자바(Java) 추억의 2048게임
풀이 백준의 2048문제와 다르게 상하좌우중에 한 방향으로 한번만 이동하면 되는 문제이다. 따라서 100개 케이스 2초 즉 1케이스당 200만 복잡도까지 사용가능하다. 방향을 입력받고 각 방향에
developerjisu.tistory.com
이제 추가해야할 부분은 5번 이동했을때의 최대값을 구하는 과정이다. 그 과정에서 배열 복사가 필요하고 이때 배열은 2단 배열이므로 깊은 복사가 필요하다. 깊은 복사의 내용은 아래와 같다.
- 1단 배열 복사: Arrays.clone();
- 2단 배열 복사: clone을 바로 해버리면 1단 배열들을 참조하는 2단 배열을 clone하는거다. 따라서 1단 배열들을 클론해줘서 2단 배열을 만들어야한다.
private static int[][] deepCopy(int[][] original) {
int[][] copy = new int[N][];
for (int i = 0; i < N; i++) {
copy[i] = original[i].clone(); // 각 행을 개별적으로 복사
}
return copy;
}
이후에 deque의 값과 칸의 값이 더해질때 큰값이 나올수있기 때문에 중간에 max = Math.max(a, b)넣어준다. 추가로 처음 입력받은 값이 제일 클 수 있기때문에 max는 map값 입력받을때 제일 큰값으로 지정한다.
추가 구현(최적화)
이 문제의 다음단계인 "2048(Hard)"를 풀기 위해 고민한 방법이다. 실제로는 시간초과로 통과하지는 못했지만 2048(Easy)의 실행시간은 줄였다. 움직여도 변하지않은 경우가 있다. 이제 좌우 한번씩 움직여도 동일한 맵과 상하 한번씩 움직여도 동일한 맵이있다. 그 경우를 판별하고자 flag를 4개 두고 상하, 또는 좌우가 둘다 false이면 return시켜 더이상 가지치가 작동하지 않도록 했다.
예제 입력 예시
3
2 2 2
4 4 4
8 8 8
예제 출력 예시
16
소스코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.StringTokenizer;
//12094번
public class Main {
private static int N, max;
private static boolean[] change; //상하좌우 변경된 경우 확인
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
N = Integer.parseInt(br.readLine());
max = 0;
// map입력받기
int[][] originalMap = new int[N][N];
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++) {
originalMap[i][j] = Integer.parseInt(st.nextToken());
max = Math.max(max, originalMap[i][j]);
}
}
change = new boolean[4];
Arrays.fill(change, true);
perm(originalMap, 0);
System.out.println(max);
}
private static void perm(int[][] map, int cnt) {
if (cnt == 5) return;
int[][] cloneMap1 = deepCopy(map);
change[0] = false;
play2048(cloneMap1, false, 0, N, 1, cnt, 0); //up
change[0] = true;
int[][] cloneMap2 = deepCopy(map);
change[1] = false;
play2048(cloneMap2, false, N - 1, -1, -1, cnt, 1); //down
change[1] = true;
int[][] cloneMap3 = deepCopy(map);
change[2] = false;
play2048(cloneMap3, true, N - 1, -1, -1, cnt, 2); //right
change[2] = true;
int[][] cloneMap4 = deepCopy(map);
change[3] = false;
play2048(cloneMap4, true, 0, N, 1, cnt, 3); //left
change[3] = true;
}
private static int[][] deepCopy(int[][] original) {
int[][] copy = new int[N][];
for (int i = 0; i < N; i++) {
copy[i] = original[i].clone(); // 각 행을 개별적으로 복사
}
return copy;
}
private static void play2048(int map[][], boolean goX, int s, int e, int m, int cnt, int changeNum) {
int ys = 0;
int xs = 0;
if (goX) xs = s;
else ys = s;
while (!((goX) ? ys == N : xs == N)) {
Deque<Integer> d = new LinkedList<>();
boolean flag = false; //true면 이전에서 합쳐진경우
if (goX) xs = s;
else ys = s;
while (!((goX) ? xs == e : ys == e)) {
if (map[ys][xs] == 0) {
} else if (d.isEmpty()) d.add(map[ys][xs]); // 비어있는 경우
else if (flag || d.peekLast() != map[ys][xs]) { // 이전에서 합쳐진 경우 또는 현개값과 스택의 peek이 다른경우
d.add(map[ys][xs]);
flag = false;
} else if (map[ys][xs] != 0) { //flag가 false이고 같은 경우
int num = d.pollLast()*2;
d.add(num);
max = Math.max(max, num);
flag = true;
change[changeNum] = true;
}
map[ys][xs] = 0;
if (goX) xs += m;
else ys += m;
}
if((!change[0]&&!change[1])||(!change[2]&&!change[3])) return;
//빈칸 채워주기
if (goX) {
int xIdx = s;
while (!d.isEmpty()) {
map[ys][xIdx] = d.poll();
xIdx += m;
}
ys++;
} else {
int yIdx = s;
while (!d.isEmpty()) {
map[yIdx][xs] = d.poll();
yIdx += m;
}
xs++;
}
}
perm(map, cnt + 1);
}
}
문제 출처
https://www.acmicpc.net/problem/12100
'알고리즘 > 자바' 카테고리의 다른 글
[백준 알고리즘] 17070번 자바(Java) 파이프 옮기기 1 (0) | 2024.09.06 |
---|---|
[백준 알고리즘] 2931번 자바(Java) 가스관 (0) | 2024.08.29 |
[SWEA] 6109번 자바(Java) 추억의 2048게임 (0) | 2024.08.22 |
[백준 알고리즘] 12015번 자바(Java) 가장 긴 증가하는 부분 수열2 (0) | 2024.08.17 |
[백준 알고리즘] 9252번 자바(Java) LCS2 (0) | 2024.07.29 |