May 23 2008

자기복제수 문제

분류: DS/Algorithm 태그: ,, Heart @ 3:18 오후

Trackback : http://dev.heartsavior.net/archives/142/trackback/

프로그래밍 갤러리에서 다시 자기복제수 떡밥이 돌길래 덥썩 물고 구현해 봤다.
(참고로 자기복제수란, N을 제곱했을 때 맨 뒷자리에 N이 다시 나타나는 수를 말한다. 예를 들어, 76은 자기복제수이다. 76 * 76 = 57′76′ 이기 때문이다.)

프갤의 떡밥은 자기복제수를 계속 찾아나가면서 몇이 나왔을 때 사양이 어떻게 되고 몇 ms 가 걸렸는지인데, 난 그냥 회사에서 잠깐 짬 내서 하는 것이니까 구글링하다 찾은 문제의 스펙대로만 풀었다. 그래서 큰 수의 처리도 없고(N의 제곱이 INT_MAX보다 작아야 함) 처리 속도도 크게 신경 안썼다. 감으로 바로 떠오른 알고리즘을 통해 풀어 보았다.

package practice;
 
import java.util.Scanner;
 
/**
 * '자기복제수' 체크<br />
 * <br />
 * 자기복제수 : 어떤 자연수 N 을 제곱했을 때, 그 제곱수의 맨 뒷자리에 원래의 수 N이 다시 나타나는 경우<br />
 * 그 수 N을 자기복제수라고 한다.<br />
 * <br />
 * 입력<br />
 * 입력은 표준입력(standard input)을 통해 받아들인다. <br />
 * 입력의 첫 줄에는 테스트 케이스의 개수 T (1 ≤ T ≤ 20)가 주어진다. <br />
 * 각 테스트 케이스는 한 줄로 이루어져 있으며 자연수 N (1 ≤ N ≤ 1000) 이 주어진다.<br />
 * <br />
 * 출력<br />
 * 출력은 표준출력(standard output)을 통하여 출력한다. <br />
 * 각 테스트 케이스에 대해, 주어진 자연수가 자기복제수이면 YES를 아니면 NO를 출력한다.<br /> 
 */
public class PracticeSelfCloning {
	public static final int MAX_INPUT_NUM = 1000;
	public static final int MIN_INPUT_NUM = 1;
 
	public static final int MAX_INPUT_COUNT = 20;
	public static final int MIN_INPUT_COUNT = 1;
 
	private int inputCount = 0;
	private int[] inputDataList = null;
 
	public PracticeSelfCloning() {
		inputCount = 0;
		inputDataList = null;
	}
 
	public boolean inputData( ) {
		Scanner scan = new Scanner(System.in);
		inputCount = scan.nextInt();
 
		if( inputCount > MAX_INPUT_NUM || inputCount < MIN_INPUT_COUNT ) {
			System.err.println("테스트 케이스의 갯수가 " + MIN_INPUT_COUNT + 
					"<=T<=" + MAX_INPUT_COUNT + " 을 만족하지 않습니다.");
			return false;
		}
 
		inputDataList = new int[inputCount];
 
		for( int i = 0 ; i < inputCount ; i++ ) {
			inputDataList[i] = scan.nextInt();
 
			if( inputDataList[i] > MAX_INPUT_NUM || inputDataList[i] < MIN_INPUT_NUM ) {
				System.err.println("테스트 케이스가 " + MIN_INPUT_NUM + 
						"<=N<=" + MAX_INPUT_NUM + " 을 만족하지 않습니다.");
				return false;
			}
		}
 
		scan.close();
 
		return true;
	}
 
	public void printCheckSelfCloning() {
 
		for ( int i = 0 ; i < inputCount ; i++ ) {
			if( checkSelfCloning( inputDataList[i] ) )
				System.out.println("YES");
			else
				System.out.println("NO");
		}
	}
 
	private boolean checkSelfCloning(int value) {
		int squareVal = value * value;
		int valueOri = value;
 
		/* 
		 * 알고리즘 : 원래의 수를 N이라 하고 N의 자릿수를 c라 하면,
		 * N^2를 10^c로 나눈 나머지가 N이 되어야 자기복제 수이다.
		 */
		int modVal = 10;
		int valueCipher = 1;
		while( value / 10 > 0 ) {
			valueCipher++;
			value = value / 10;
			modVal = modVal * 10;
		}
 
		int mod = squareVal % modVal;
		if( mod == valueOri )
			return true;
 
		return false;
	}
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		if( !checkCondition() )
			return;
 
		PracticeSelfCloning selfCloning = new PracticeSelfCloning();
 
		selfCloning.inputData();
		selfCloning.printCheckSelfCloning();		
	}
 
	public static boolean checkCondition() {
		// MAX_INPUT_NUM * MAX_INPUT_NUM 이 int 의 한계를 넘어설 경우를 체크
		if( Integer.MAX_VALUE / MAX_INPUT_NUM < MAX_INPUT_NUM ) {
			System.err.println("MAX_INPUT_NUM 의 값을 낮추거나 BigInteger를 사용해야 합니다.");
			return false;
		}
 
		// MAX_INPUT_COUNT 는 체크할 필요 없음 -> 컴파일 시에 오버플로우 체크
		return true;
	}
}

아마도 많은 최적화 방법이 있지 않을까 생각한다.


May 23 2008

Dijkstra 관련 문제 하나…(출처 : 프로그래밍 갤러리)

분류: DS/Algorithm 태그: ,, , , Heart @ 12:40 오전

Trackback : http://dev.heartsavior.net/archives/140/trackback/

프로그래밍 갤러리에서 올라온 문제를 한 번 풀어 보았다.
실무 작업만 하다가 오랜만에 자료구조/알고리즘 문제를 푸니까 좀 어렵기도 하고 전공 기억도 가물가물했다.
사실 아무런 정보도 없었으면 좀 헤맬 뻔 했는데, 리플에 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();
		}
 
	}
 
}

May 23 2008

(IBM DW)코드 트레이닝 Part2. 알고리즘과 성능 - 트랙백

분류: DS/Algorithm 태그: ,, Heart @ 12:11 오전

Trackback : http://dev.heartsavior.net/archives/139/trackback/

IBM developerWorks에서 재밌는 기사를 보아서 회사 일도 얼추 끝냈고 해서 잠깐 시간 내서 풀어봤다.
음… 문제는 상당히 쉽다. 학부 1~2학년 과제 수준인 것 같다.
물론 그냥 풀 때의 얘기이고, 다른 알고리즘 방법을 찾는다던지 하면 더 걸릴 수도 있을 듯…
다른 알고리즘 찾는 건 시간 날 때 해봐야지. 일단 일반적인 알고리즘으로 1~4를 해결했다.

솔직히 얘기하자면, 문제를 읽어보자 마자 일반적인 알고리즘이 생각났고 새로운 알고리즘 찾는 게 귀찮았다.
(그래서 일반적인 알고리즘으로 푼 것이다. ㅜ.ㅜ 그리고 다른 방법이 딱히 생각나지도 않는다. 다른 방법이 있긴 한건가…;;)
음… 이러면 안되는데…

내 풀이 ..

퀴즈 1. Both

    public static ArrayList both(ArrayList xs, ArrayList ys) {
            ArrayList bothList = new ArrayList();
            Object y = null;
            // 먼저 첫 번째 리스트를 몽땅 저장
            bothList.addAll(xs);
            Iterator elems = ys.iterator();
            while( elems.hasNext() ) {
                y = elems.next();
                // 첫 번째 리스트에 값이 없을 경우에만 추가
                if( !xs.contains(y) )
                    bothList.add(y);
            }
            return bothList;
        }

: 코드를 보면 알겠지만 Either()랑 거의 똑같다 -_-;;

퀴즈 2. fastEither

    public static ArrayList fastEither(ArrayList xList, ArrayList yList) {
        ArrayList eitherList = new ArrayList();
        final int xSize = xList.size();
        final int ySize = yList.size();
        Comparable x, y;
        int order, xpos, ypos;
        xpos = ypos = 0;
        while( xpos &lt; xSize &amp;&amp; ypos &lt; ySize ) {
            x = (Comparable)xList.get(xpos);
            y = (Comparable)yList.get(ypos);
            order = x.compareTo(y);
            if( order &lt; 0 ) {            // x가 작음
                xpos++;
            } else if( order &gt; 0 ) {    // y가 작음
                ypos++;
            } else {                    // x = y
                eitherList.add(x);
                xpos++;
                ypos++;
            }
        }
        return eitherList;
    }

: 마찬가지로 코드를 보면 알겠지만 fastBoth()랑 거의 똑같다 -_-;;

퀴즈 3. isSubstring

    public static boolean isSubstring(char[] left, char[] right) {
        final int leftCount = left.length;
        final int rightCount = right.length;
        for( int i = 0 ; i &lt; rightCount ; i++ ) {
            if( left[0] == right[i] ) {
                boolean same = true;
                for( int j = 1 ; j &lt; leftCount ; j++ ) {
                    if( left[j] != right[i+j] ) {
                        same = false;
                        break;
                    }
                }
                if( true == same )
                    return true;
            }
        }
        return false;
    }

: 첫 글자 일치하면 2, 3, … , n 번째 글자까지 맞는지 확인

퀴즈 4. match

    public class MatchTest {
        private static final String KIND_OF_BRACKETS_LEFT = "([< {";
        private static final String KIND_OF_BRACKETS_RIGHT = ")]>}";
 
        public static boolean isLeftBrackets(char ch) {
            if( -1 == KIND_OF_BRACKETS_LEFT.indexOf(ch) )
                return false;
 
            return true;
        }
 
        public static boolean isRightBrackets(char ch) {
            if( -1 == KIND_OF_BRACKETS_RIGHT.indexOf(ch) )
                return false;
 
            return true;
        }
 
        public static boolean isBracketMatch(char leftBracket, char rightBracket) {
            int leftBracketIdx = KIND_OF_BRACKETS_LEFT.indexOf(leftBracket);
            int rightBracketIdx = KIND_OF_BRACKETS_RIGHT.indexOf(rightBracket);
 
            if( (-1 == leftBracketIdx) || (-1 == rightBracketIdx) )
                return false;
 
            return (leftBracketIdx == rightBracketIdx);
        }
 
        public static boolean matchUseStack(char[] cs) {
            final int length = cs.length;
            if( 0 == length )
                return false;        // 길이가 0인 경우도 짝이 없다고 가정함
 
            /*
             * ArrayList로 할 수도 있지만 배열 사이즈가 어느 정도 정해져 있기 때문에 이렇게 함
             * cs의 크기가 크다면 ArrayList가 나을 수도 있음(하지만 ArrayList를 사용하면 Object로 저장해야 함)
             *
             * Stack의 길이는 cs의 길이의 반이면 됨, 그 이상 넘어가면 짝이 있을 수 없음.
             */
            int stackLen = length / 2;
 
            char[] stack = new char[stackLen];
 
            int stackIdx = 0;
            for( int i = 0 ; i < length ; i++ ) {
                char ch = cs[i];
 
                if( isLeftBrackets(ch) ) {
                    // Stack에 집어넣음
                    if( stackIdx >= stackLen ) {
                        // Stack이 꽉 찼음. 짝이 없음을 의미
                        return false;
                    }
 
                    stack[stackIdx] = ch;
                    stackIdx++;
 
                } else if( isRightBrackets(ch) ) {
                    // Stack에서 하나를 빼서 비교
                    if( stackIdx >= 0 ) {
                        stackIdx--;
                        char stackCh = stack[stackIdx];
 
                        // 꺼낸 괄호가 짝이 아니면 짝이 아님을 리턴
                        if( !isBracketMatch(stackCh,ch) )
                            return false;
                    } else {
                        // Stack에서 꺼낼 왼쪽 괄호가 없음. 짝이 없음을 의미
                        return false;
                    }
                }
                // 괄호 종류가 아니면 무시
            }
            return true;
        }
 
        public static boolean match(char[] cs) {
            return matchUseStack(cs);
        }
 
        public static void main(String[] args) {
            char[] first = { '(', '[', '< ', '{', '}', '>', ']', ')' };
            char[] second = { '(', '[', '< ', '{', '>', '}', ']', ')' };
            char[] third = { '(', 'a', 'c', ')', '[', '{', '}', ']' };
 
            System.out.println( match(first) ); // yes
            System.out.println( match(second) ); // no
            System.out.println( match(third) ); // yes
        }
    }

: Stack을 이용해서 left-bracket은 Stack에 쭉 넣고, right-bracket이 나오면 Stack에서 꺼내서 비교하는 알고리즘(이라고 하기도 민망;;)