May 23 2008
Dijkstra 관련 문제 하나…(출처 : 프로그래밍 갤러리)
프로그래밍 갤러리에서 올라온 문제를 한 번 풀어 보았다.
실무 작업만 하다가 오랜만에 자료구조/알고리즘 문제를 푸니까 좀 어렵기도 하고 전공 기억도 가물가물했다.
사실 아무런 정보도 없었으면 좀 헤맬 뻔 했는데, 리플에 Dijkstra 얘기가 나와서 해결 실마리를 찾을 수 있었다.
브로디에게 납치를 당한 뒤 창고에 갇힌 다오는 서둘러 탈출할 방법을 찾고 있다.
가로막힌 블록을 맨손으로 부수며 길을 만들어야 하는데 최소 몇 개의 블록을 부수어야 출구에 도달할 수 있을까?
이동은 상하좌우 네 방향으로만 가능하며, 블록이 여러 겹으로 놓인 장애물도 존재한다.하나의 프로그램을 작성하여 첨부 파일 map1.txt, map2.txt, map3.txt의 세 가지 입력에 대하여 각각 답을 구하시오.
입력<
5 6 // 행의 수 n, 열의 수 m
s.#… // 첫 번째 행
..#### // 두 번째 행
#.#..# // 각 행은 m개의 문자로 이루어져 있고, 각 문자의 의미는 아래와 같다..###. // 빈 공간: . 블록: # 다오: s 출구: e
###..e // n번째 행
출력2 // 탈출하기 위해 부수어야 하는 최소 블록 개수
일단 창고의 각 공간을 노드로 보면, 창고는 bi-directional weight 가 존재하는 그래프이다.
(bi-directional로 해야 하는 이유는, 두 개의 노드가 연결되는 간선이 빈 공간 -> 블록, 블록 -> 빈 공간의 서로 다른 weight가 생길 수 있기 때문이다.)
그래프에 대해 블록 노드로 가는 간선의 weight는 1, 나머지는 0 으로 매겨서 인접 행렬을 만든 다음, Dijkstra 최소거리 알고리즘을 적용하면 s -> e에 대한 최소 weight가 구해진다.
위에서 블록 노드로 가는 경우에만 1의 weight를 적용하였으므로 최소 weight가 부순 블록의 갯수가 된다.
자바로 구현해 봤다. 사실 좀 걸렸다. 코드 정리하면서 코딩하기도 했고, Dijkstra도 다시 상기시켜서 이해하는 데 시간이 좀 걸렸다.(설명이 쉽게 되어 있는 포스트가 있어 도움이 많이 됐다.) 전공 공부를 좀 열심히 해야 할 듯…
package practice; import java.io.*; import java.util.*; /** * 미로를 탈출하는 데, 출발 지점에서 도착 지점까지 최소한의 벽만 깨고 도달하는 것이 목적. * 출발 지점 -> 도착 지점까지 깨야 하는 벽의 최소 갯수를 구하라 * * Input 파일의 내용 * 첫 라인 : 행 열 * 2~(행+1) 라인 : 열 만큼의 s | . | # | e * s가 시작 위치, e 가 끝 위치 * .은 지나갈 수 있는 길, #는 벽 * * Input 파일의 내용 예제 * 5 6 * s.#... * ..#### * #.#..# * ..###. * ###..e * * 예제에 대한 답 : 2 */ public class PracticeDijkstra { /** 이어지지 않은 거리(무게) */ public static final int UNTOUCHABLE_DISTANCE = 99; /** 입력 파일명 */ public static final String INPUT_FILE_NAME = "input.txt"; /** 미로의 가로 */ private int width = 0; /** 미로의 세로 */ private int height = 0; /** 미로의 전체 칸 갯수 */ private int nodeCount = 0; /** 미로 시작 지점(칸 위치 - 1차원으로 계산) */ private int startIdx = 0; /** 미로 끝 지점(칸 위치 - 1차원으로 계산) */ private int endIdx = 0; /** 인접 행렬 */ private int [][] adjacentMatrix = null; /** 작업된 집합 */ private ArrayList proceedList = null; /** 작업 대기 집합 */ private ArrayList readyList = null; public PracticeDijkstra() { initValues(); } private void initValues() { // Initialize adjacentMatrix = null; proceedList = null; readyList = null; width = 0; height = 0; nodeCount = 0; startIdx = 0; endIdx = 0; } private boolean initData(String filePath) throws FileNotFoundException, IOException, NumberFormatException { char [][] dataMatrix = loadDataMatrix(filePath); if( null == dataMatrix ) return false; // 인접 행렬 만들기 makeAdjacentMatrix(dataMatrix); return true; } private void makeAdjacentMatrix(char [][] dataMatrix) { int height = dataMatrix.length; int width = dataMatrix[0].length; nodeCount = height * width; adjacentMatrix = new int[nodeCount][nodeCount]; for( int i = 0 ; i < height ; i++ ) { for( int j = 0 ; j < width ; j++ ) { int idx = (i * width) + j; // current node's number(index) // Mark All Node is Untouchable Distance for( int k = 0 ; k < height * width ; k++ ) adjacentMatrix[idx][k] = UNTOUCHABLE_DISTANCE; // 4-way check, including myself adjacentMatrix[idx][idx] = 0; int checkIdx = 0; // left if( j > 0 ) { checkIdx = (i * width) + j - 1; if( dataMatrix[i][j-1] == '#' ) adjacentMatrix[idx][checkIdx] = 1; else adjacentMatrix[idx][checkIdx] = 0; } // right if( j + 1 < width ) { checkIdx = (i * width) + j + 1; if( dataMatrix[i][j+1] == '#' ) adjacentMatrix[idx][checkIdx] = 1; else adjacentMatrix[idx][checkIdx] = 0; } // up if( i > 0 ) { checkIdx = ( (i - 1) * width) + j; if( dataMatrix[i - 1][j] == '#' ) adjacentMatrix[idx][checkIdx] = 1; else adjacentMatrix[idx][checkIdx] = 0; } // down if( i + 1 < height ) { checkIdx = ( (i + 1) * width) + j; if( dataMatrix[i + 1][j] == '#' ) adjacentMatrix[idx][checkIdx] = 1; else adjacentMatrix[idx][checkIdx] = 0; } } } // test //printMatrix(adjacentMatrix); } private char[][] loadDataMatrix(String filePath) throws FileNotFoundException, IOException, NumberFormatException { BufferedReader br = new BufferedReader( new InputStreamReader( new FileInputStream(filePath)) ); String dataSizeLine = br.readLine(); if( null == dataSizeLine ) { System.err.println("파일 내용이 없습니다."); return null; } StringTokenizer st = new StringTokenizer(dataSizeLine, " "); if( 2 != st.countTokens() ) { System.err.println("파일 첫 라인의 형식이 맞지 않습니다.(파일의 첫 라인은 '행 열' 만 적혀 있어야 함)"); return null; } height = Integer.parseInt( st.nextToken() ); width = Integer.parseInt( st.nextToken() ); if( 0 == height || 0 == width ) { System.err.println("행과 열은 1 이상이어야 합니다."); return null; } st = null; char [][] dataMatrix = new char[height][width]; for( int i = 0 ; i < height ; i++ ) { String line = br.readLine(); if( null == line ) { System.err.println("라인 수가 부족합니다."); return null; } for( int j = 0 ; j < width ; j++ ) { char ch = line.charAt(j); if( ch != '.' && ch != '#' && ch != 's' && ch != 'e' ) { System.err.println("데이터가 올바르게 입력되지 않았습니다."); return null; } if( 's' == ch ) { startIdx = (i * width) + j; // write start node's index ch = '.'; } else if( 'e' == ch ) { endIdx = (i * width) + j; // write end node's index ch = '.'; } dataMatrix[i][j] = ch; } } br.close(); br = null; // dataMatrix Load Complete // test //printMatrix(dataMatrix); return dataMatrix; } private void printMatrix(int [][] matrix) { int height = matrix.length; if( height <= 0 ) return; int width = matrix[0].length; if( width <= 0 ) return; for( int i = 0 ; i < height ; i++ ) { for( int j = 0 ; j < width ; j++ ) { System.out.print(matrix[i][j] + " "); } System.out.println(); } } private void printMatrix(char [][] matrix) { int height = matrix.length; if( height <= 0 ) return; int width = matrix[0].length; if( width <= 0 ) return; for( int i = 0 ; i < height ; i++ ) { for( int j = 0 ; j < width ; j++ ) { System.out.print(matrix[i][j] + " "); } System.out.println(); } } public int calculateShortestPath(String filePath) throws FileNotFoundException, IOException, NumberFormatException { initValues(); if( !initData(filePath) ) return UNTOUCHABLE_DISTANCE; // Dijkstra proceedList = new ArrayList(); readyList = new ArrayList(); // A - Start proceedList.add(startIdx); for( int i = 0 ; i < nodeCount ; i++ ) { if( i != startIdx ) readyList.add(i); } int weightList[] = adjacentMatrix[startIdx].clone(); int proceedTotalWeight = 0; int proceedLastIdx = startIdx; while( readyList.size() > 0 ) { int minIdx = 0; int minVal = UNTOUCHABLE_DISTANCE; proceedLastIdx = proceedList.get( proceedList.size() - 1 ); Iterator iterReady = readyList.iterator(); while( iterReady.hasNext() ) { int readyIdx = iterReady.next().intValue(); int newWeight = proceedTotalWeight + adjacentMatrix[proceedLastIdx][readyIdx]; if( adjacentMatrix[startIdx][readyIdx] > newWeight ) { adjacentMatrix[startIdx][readyIdx] = newWeight; } if( minVal > adjacentMatrix[startIdx][readyIdx] ) { minIdx = readyIdx; minVal = adjacentMatrix[startIdx][readyIdx]; } } // minIdx를 readyList에서 뺌 readyList.remove(new Integer(minIdx)); proceedList.add(minIdx); proceedTotalWeight = minVal; proceedLastIdx = minIdx; } return adjacentMatrix[startIdx][endIdx]; } public static void main(String[] args) { // TODO Auto-generated method stub PracticeDijkstra dijkstra = new PracticeDijkstra(); int shortestWeight = PracticeDijkstra.UNTOUCHABLE_DISTANCE; try { shortestWeight = dijkstra.calculateShortestPath(INPUT_FILE_NAME); if( UNTOUCHABLE_DISTANCE == shortestWeight ) { System.out.println("Not connected"); } else { System.out.println("Shortest path's weight : " + shortestWeight); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NumberFormatException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
