일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- DP
- 그리디
- 코딩테스트준비
- 동적 프로그래밍
- 프로그래머스
- 완전탐색
- BinarySearch
- 우선순위큐
- 백트래킹
- 데이터베이스
- 알고리즘
- 스프링
- 항해99
- 정렬
- Spring
- 자바
- Til
- 브루트포스
- 그래프 이론
- 스프링 핵심 원리 - 기본편
- 네트워크 계층
- 트리
- 그래프
- lower bound
- 99클럽
- 개발자취업
- 백준
- Java
- DFS
- BFS
- Today
- Total
AtraFelis's Develop Diary
[99클럽 코테스터디 설날 TIL / 백준] 17825 - 주사위 윷놀이 (JAVA) 본문
99클럽 코테스터디 설날 보너스 문제 TIL
KeyWord : DFS, BackTracking, 브루트포스
문제
주사위 윷놀이는 다음과 같은 게임판에서 하는 게임이다.
- 처음에는 시작 칸에 말 4개가 있다.
- 말은 게임판에 그려진 화살표의 방향대로만 이동할 수 있다. 말이 파란색 칸에서 이동을 시작하면 파란색 화살표를 타야 하고, 이동하는 도중이거나 파란색이 아닌 칸에서 이동을 시작하면 빨간색 화살표를 타야 한다. 말이 도착 칸으로 이동하면 주사위에 나온 수와 관계 없이 이동을 마친다.
- 게임은 10개의 턴으로 이루어진다. 매 턴마다 1부터 5까지 한 면에 하나씩 적혀있는 5면체 주사위를 굴리고, 도착 칸에 있지 않은 말을 하나 골라 주사위에 나온 수만큼 이동시킨다.
- 말이 이동을 마치는 칸에 다른 말이 있으면 그 말은 고를 수 없다. 단, 이동을 마치는 칸이 도착 칸이면 고를 수 있다.
- 말이 이동을 마칠 때마다 칸에 적혀있는 수가 점수에 추가된다.
주사위에서 나올 수 10개를 미리 알고 있을 때, 얻을 수 있는 점수의 최댓값을 구해보자.
입력
첫째 줄에 주사위에서 나올 수 10개가 순서대로 주어진다.
출력
얻을 수 있는 점수의 최댓값을 출력한다.
풀이
내가 생각하기에 이 문제의 어려운 점은 두 가지다.
- 점수의 최댓값을 어떻게 구할 것인가. 즉, 이 문제의 핵심이 되는 알고리즘
- 윷놀이 판을 어떻게 구성할지
먼저 순서대로 하나씩 살펴보자.
방법론
고려했던 알고리즘은 총 세 가지였다.
- 그리디
- 동적 프로그래밍
- 백트래킹
일단 그리디의 경우에는 현재 턴에서 최댓값을 만드는 최선의 선택이 무엇인지 알 수 있는 방법이 없으므로 바로 생각에서 지웠다.
두 번째로 동적 프로그래밍은 사용할 수 있는 말은 네 개, 총 10턴을 진행하니, DP[4][10]
이런 느낌으로 선언하면 DP로 풀 수 있지 않을까? 하는 생각이었다.
하지만 DP는 복잡한 문제를 더 작은 하위 문제로 쪼개어 해결하는 알고리즘이다. 여기서 더 작은 하위 문제로 쪼갤 수 있는 문제가 있을까... 하면, 내 눈에는 쪼갤 수 있을 법한 문제는 보이지 않았다.
마지막으로 백트래킹. 잠시 고민하다가 의사코드를 작성해보았다.
if (depth == 10) {
ans = max(ans, score);
} else {
기존의 말 혹은 새로운 말을 움직인다.
backtracking(depth + 1);
기존 위치로 복구.
}
이렇게 백트래킹을 이용하여 브루트포스를 하면 해를 구할 수 있을 것이다.
이렇게 했을 때 발생하기 가장 쉬운 문제는 시간 초과 혹은 메모리 초과다. 하지만 이 문제는 탐색을 진행하는 범위가 50도 안 되므로 효율성으로 인한 문제는 발생하지는 않을 것으로 보였다.
윷놀이 판
파란색 칸이고 빨간색 칸이고, 칸에 말이 있고 없고, 이런 걸 떠나서 윷놀이 판 자체가 깔끔하게 정리되어 있지 않으니, 일단 윷놀이 판에 번호부터 매겨보자.
이제 이 윷놀이 판을 코드 상에서 어떻게 표현할지가 문제였다.
int[] map = new int[33];
이렇게 배열을 선언해서 사용할까? 했지만, 이렇게 하면 너무 헷갈릴 것 같았다.
일단, 말이 이동하고자 하는 칸 위에 이미 말이 있는지 확인해야 하고, 이 칸에 몇 점의 점수가 배정되어 있는지 확인해야 하며, 현재 칸이 파란색 칸인지 확인해야 하고... 일단 고려할게 너무 많았다.
그렇기에 각 칸의 정보를 저장하는 클래스를 만들어 사용하기로 했다.
static class Node {
int score; // 칸에 적힌 점수
int nextRed; // 빨간색 경로로 이동 시 다음 칸의 인덱스
int nextBlue; // 파란색 경로로 이동 시 다음 칸의 인덱스 (없으면 -1)
boolean isEmpty = true;
Node(int score, int nextRed, int nextBlue) {
this.score = score;
this.nextRed = nextRed;
this.nextBlue = nextBlue;
}
}
윷놀이 판 초기화는 하드코딩이 될 수밖에 없었다. 워낙 규칙성이 없다보니 여기서 값 잘못 입력한 것 때문에 찾다가 30분은 날려먹었다.
private static void initMap() {
for (int i = 0; i < 20; i++) {
map[i] = new Node(i*2, i+1, -1);
}
map[5].nextBlue = 21;
map[10].nextBlue = 24;
map[15].nextBlue = 26;
map[20] = new Node(40, 32, -1);
map[21] = new Node(13, 22, -1);
map[22] = new Node(16, 23, -1);
map[23] = new Node(19, 29, -1);
map[24] = new Node(22, 25, -1);
map[25] = new Node(24, 29, -1);
map[26] = new Node(28, 27, -1);
map[27] = new Node(27, 28, -1);
map[28] = new Node(26, 29, -1);
map[29] = new Node(25, 30, -1);
map[30] = new Node(30, 31, -1);
map[31] = new Node(35, 20, -1);
map[32] = new Node(0, 32, -1);
}
또 말에 관한 클래스도 정의했다. 이 클래스에는 이 말의 위치와 현재까지 이 말이 획득한 점수를 저장한다.
static class Piece {
int score;
int currPos;
public Piece(int score, int currPos) {
this.score = score;
this.currPos = currPos;
}
}
이제 로직만 구현하면 된다. 고려할 부분이 좀 있으므로 헷갈리지 않게 정리한 후 위의 백트래킹 의사코드에 적용하면 해결할 수 있다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
static class Node {
int score; // 칸에 적힌 점수
int nextRed; // 빨간색 경로로 이동 시 다음 칸의 인덱스
int nextBlue; // 파란색 경로로 이동 시 다음 칸의 인덱스 (없으면 -1)
boolean isEmpty = true;
Node(int score, int nextRed, int nextBlue) {
this.score = score;
this.nextRed = nextRed;
this.nextBlue = nextBlue;
}
}
static class Piece {
int score;
int currPos;
public Piece(int score, int currPos) {
this.score = score;
this.currPos = currPos;
}
}
static int[] diceValues = new int[10];
static Node[] map = new Node[33];
static Piece[] pieces = new Piece[4];
static int ans = 0;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer stk = new StringTokenizer(br.readLine());
initMap();
initPiece();
for (int i = 0; i < 10; i++) {
diceValues[i] = Integer.parseInt(stk.nextToken());
}
backTracking(0);
System.out.println(ans);
}
private static void backTracking(int depth) {
if (depth == 10) {
int score = 0;
for (int i = 0; i < 4; i++) {
score += pieces[i].score;
}
ans = Math.max(ans, score);
} else {
for (int i = 0; i < 4; i++) {
if(pieces[i].currPos == 32)
continue;
int diceValue = diceValues[depth];
int tmp = pieces[i].currPos;
// 파란색 칸이라면
if (map[pieces[i].currPos].nextBlue != -1) {
diceValue--;
pieces[i].currPos = map[pieces[i].currPos].nextBlue;
}
// 남은 value만큼 빨간색 화살표를 따라 이동
for (int j = 0; j < diceValue; j++) {
pieces[i].currPos = map[pieces[i].currPos].nextRed;
}
pieces[i].score += map[pieces[i].currPos].score;
// 이동하고자 하는 노드가 비어있다면
if(map[pieces[i].currPos].isEmpty) {
if(pieces[i].currPos != 32)
map[pieces[i].currPos].isEmpty = false;
map[tmp].isEmpty = true;
backTracking(depth + 1);
map[tmp].isEmpty = false;
map[pieces[i].currPos].isEmpty = true;
}
pieces[i].score -= map[pieces[i].currPos].score;
pieces[i].currPos = tmp;
}
}
}
private static void initMap() {
for (int i = 0; i < 20; i++) {
map[i] = new Node(i*2, i+1, -1);
}
map[5].nextBlue = 21;
map[10].nextBlue = 24;
map[15].nextBlue = 26;
map[20] = new Node(40, 32, -1);
map[21] = new Node(13, 22, -1);
map[22] = new Node(16, 23, -1);
map[23] = new Node(19, 29, -1);
map[24] = new Node(22, 25, -1);
map[25] = new Node(24, 29, -1);
map[26] = new Node(28, 27, -1);
map[27] = new Node(27, 28, -1);
map[28] = new Node(26, 29, -1);
map[29] = new Node(25, 30, -1);
map[30] = new Node(30, 31, -1);
map[31] = new Node(35, 20, -1);
map[32] = new Node(0, 32, -1);
}
private static void initPiece() {
for (int i = 0; i < pieces.length; i++) {
pieces[i] = new Piece(0, 0);
}
}
}
'Algorithm > 백준' 카테고리의 다른 글
[99클럽 코테 스터디 11일차 TIL / 백준] 1018 - 체스판 다시 칠하기 (브루트포스) (0) | 2025.02.03 |
---|---|
[백준] 1043 - 거짓말 (JAVA) (0) | 2025.01.28 |
[99클럽 코테 스터디 10일차 TIL / 백준] 2573- 빙산(JAVA) (0) | 2025.01.24 |
[99클럽 코테 스터디 9일차 TIL / 백준] 1707 - 이분 그래프 (JAVA) (0) | 2025.01.23 |
[99클럽 코테 스터디 8일차 TIL / 백준] 2667 - 단지번호붙이기 (JAVA) (0) | 2025.01.22 |