-1
이 있을 경우 그 턴에는 이동하지 않는다. -1
이 아닌 경우 해당 숫자로 이동한다. (더 앞 혹은 더 뒤로 이동할 수 있다.) -1
을 반환함board = [[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1],[-1,35,-1,-1,13,-1],[-1,-1,-1,-1,-1,-1],[-1,15,-1,-1,-1,-1]]
4
기본 전략 : 방향 graph
Map<Integer, Set<Integer>>
형식으로 graph를 만든다.1단계 전략. board를 통해 graph 만들기 (makeGraph()
)
int[][]
형식의 board는 파악하기 어려우니, int[]
형식으로 전환함 (함수 getValue()
로 구현Set<Integet>
형식으로 만들어 Map
에 넣는다.-1
이면 해당 위치 아니면 value를 넣는다.2단계 전략. graph를 통해 최소 횟수 탐색 (BFS, findMinimumCount()
)
1
에서 시작함총 칸수 / 6 + 1
)만큼만 반복하고 안되면 -1
을 반환함class Solution {
private static final int MAX_DICE = 6;
private static final int NO_MOVE = -1;
private static final int NOT_FINISH = -1;
public int snakesAndLadders(int[][] board) {
int destination = board.length * board[0].length;
Map<Integer, Set<Integer>> graph = makeGraph(board);
return findMinimumCount(graph, destination);
}
private Map<Integer, Set<Integer>> makeGraph(int[][] board) {
int[] values = getValues(board);
int destination = values.length;
Map<Integer, Set<Integer>> graph = new HashMap<>();
for (int index = 0; index < values.length - 1; index++) {
Set<Integer> nextNodes = new HashSet<>();
for (int dice = 1; dice <= MAX_DICE; dice++) {
if (index + 1 + dice > destination) {
break;
}
int next = values[index + dice] == NO_MOVE ? index + 1 + dice : values[index + dice];
nextNodes.add(next);
}
graph.put(index + 1, nextNodes);
}
return graph;
}
private int[] getValues(int[][] board) {
int[] values = new int[board.length * board[0].length];
for (int i = 0; i < values.length; i++) {
values[i] = getBoardValue(board, i);
}
return values;
}
private int getBoardValue(int[][] board, int index) {
int rowIndex = index % board.length;
int columnIndex = index / board.length;
if (columnIndex % 2 == 0) {
return board[board.length - 1 - columnIndex][rowIndex];
} else {
return board[board.length - 1 - columnIndex][board.length - 1 - rowIndex];
}
}
private int findMinimumCount(Map<Integer, Set<Integer>> graph, int destination) {
Set<Integer> nodes = new HashSet<>();
nodes.add(0);
int moveCount = 1;
while (moveCount < destination) {
Set<Integer> nextNodes = new HashSet<>();
for (int node : nodes) {
if (graph.get(node).contains(destination)) {
return moveCount;
}
nextNodes.addAll(graph.get(node));
}
nodes = nextNodes;
moveCount++;
}
return NOT_FINISH;
}
}
기존 문제점 & 해결 방안
repository
를 관리하여 중복적으로 탐색하지 않도록 함Time complexity : (graph 탐색에서 중복되는 경우가 없어짐)
Space complexity :
// 나머지 함수는 1차 작성 코드와 동일
private int findMinimumCount(Map<Integer, Set<Integer>> graph, int destination) {
Set<Integer> nodes = new HashSet<>();
Set<Integer> repository = new HashSet<>();
nodes.add(0);
repository.add(0);
int move = 1;
int maxMove = destination / MAX_DICE + 1;
while (move <= maxMove) {
Set<Integer> nextNodes = new HashSet<>();
for (int node : nodes) {
if (graph.get(node).contains(destination)) {
return move;
}
for (int lowerNode : graph.get(node)) {
if (!repository.contains(lowerNode)) {
nextNodes.add(lowerNode);
repository.add(lowerNode);
}
}
}
nodes = nextNodes;
move++;
}
return NOT_FINISH;
}
getValues()
// 내 답안
private int[] getValues(int[][] board) {
int[] values = new int[board.length * board[0].length];
for (int i = 0; i < values.length; i++) {
values[i] = getBoardValue(board, i);
}
return values;
}
private int getBoardValue(int[][] board, int index) {
int rowIndex = index % board.length;
int columnIndex = index / board.length;
if (columnIndex % 2 == 0) {
return board[board.length - 1 - columnIndex][rowIndex];
} else {
return board[board.length - 1 - columnIndex][board.length - 1 - rowIndex];
}
}
// 모범 답안
private short[] getValues(int[][] board) {
short[] boardValues = new short[destination + 1];
int boardIndex = 1;
for (int row = n - 1; row >= 0; row--) {
for (int col = 0; col < n; col++) {
boardValues[boardIndex++] = (short)board[row][col];
}
row--;
if (row < 0) {
break;
}
for (int col = n - 1; col >= 0; col--) {
boardValues[boardIndex++] = (short)board[row][col];
}
}
return boardValues
}
graph를 통해 최소 횟수 탐색 방법
int[]
와 readIndex
, writeIndex
를 이용하여 Queue 방식과 유사하게 사용했다.byte[] count
를 크기를 만큼 만들어 count[node] == 1
의 형식으로 이미 방문한 노드인지 확인함// 내 답안
private int findMinimumCount(Map<Integer, Set<Integer>> graph, int destination) {
Set<Integer> nodes = new HashSet<>();
Set<Integer> repository = new HashSet<>();
nodes.add(0);
repository.add(0);
int move = 1;
int maxMove = destination / MAX_DICE + 1;
while (move <= maxMove) {
Set<Integer> nextNodes = new HashSet<>();
for (int node : nodes) {
if (graph.get(node).contains(destination)) {
return move;
}
for (int lowerNode : graph.get(node)) {
if (!repository.contains(lowerNode)) {
nextNodes.add(lowerNode);
repository.add(lowerNode);
}
}
}
nodes = nextNodes;
move++;
}
return NOT_FINISH;
}
// 모범 답안
final int bfsQueueLen = Math.min(n * n, 8 * n);
short[] bfsQueue = new short[bfsQueueLen];
int readIndex = 0;
int writeIndex = 0;
bfsQueue[writeIndex++] = 1; // Initialize BFS queue to start at square #1
byte[] count = new byte[endSquare + 1];
count[1] = 1; // Mark the starting location as already visited.
while (readIndex != writeIndex) {
int currSquare = bfsQueue[readIndex++];
readIndex %= bfsQueueLen;
int maxOpenMove = 0;
for (int move = 6; move >= 1; move--) {
int nextSquare = currSquare + move;
if (brd[nextSquare] >= 0) {
if ((nextSquare = brd[nextSquare]) == endSquare) return count[currSquare];
} else {
if (move < maxOpenMove) // If we already moved to an open square 1 to 6
continue;
maxOpenMove = move;
}
if (count[nextSquare] == 0) {
count[nextSquare] = (byte)(count[currSquare] + 1);
bfsQueue[writeIndex++] = (short)nextSquare;
if ((writeIndex %= bfsQueueLen) == readIndex) return 0; // Queue overflow
}
}
}
return -1;
모범 답안에서 문제점
개선 방안
내가 만든 Custom 자료 구조
class UniqueIntegerQueue {
private static final int ALREADY_VISIT = 1;
private final int max;
private final byte[] count;
private final int[] queue;
private int readIndex = 0;
private int writeIndex = 0;
UniqueIntegerQueue(int max) {
this.max = max;
this.count = new byte[max + 1];
this.queue = new int[max];
}
boolean add(int value) {
validateValue(value);
if (count[value] == ALREADY_VISIT) {
return false;
}
queue[writeIndex++] = value;
count[value] = ALREADY_VISIT;
return true;
}
int remove() {
if (size() <= 0) {
throw new IllegalStateException();
}
return queue[readIndex++];
}
int size() {
return writeIndex - readIndex;
}
private void validateValue(int value) {
if (value < 0 && value > max) {
throw new IllegalArgumentException();
}
}
}
Custom 자료 구조를 이용한 답안
class Solution {
private static final int NO_MOVEMENT = -1;
private static final int NOT_EXIST = -1;
private static final int MAX_DICE = 6;
public int snakesAndLadders(int[][] board) {
int destination = board.length * board[0].length;
short[] boardValues = makeGraph(board);
return findMinimumCount(boardValues, destination);
}
private short[] makeGraph(int[][] board) {
final int n = board.length;
final int destination = n * n;
short[] boardValues = new short[destination + 1];
int boardIndex = 1;
for (int row = n - 1; row >= 0; row--) {
for (int col = 0; col < n; col++) {
boardValues[boardIndex++] = (short)board[row][col];
}
row--;
if (row < 0) {
break;
}
for (int col = n - 1; col >= 0; col--) {
boardValues[boardIndex++] = (short)board[row][col];
}
}
return boardValues;
}
private int findMinimumCount(short[] boardValues, int destination) {
UniqueQueueUnderPositiveInteger queue = new UniqueIntegerQueue(destination);
queue.add(1);
int movingCount = 1;
while (queue.size() > 0) {
int size = queue.size();
for (int count = 0; count < size; count++) {
int position = queue.remove();
for (int dice = 1; dice <= MAX_DICE; dice++) {
int nextPosition = boardValues[position + dice] == NO_MOVEMENT
? position + dice : boardValues[position + dice];
if (nextPosition == destination) {
return movingCount;
}
queue.add(nextPosition);
}
}
movingCount++;
}
return NOT_EXIST;
}