Power Coding 파워 코딩

Jack goes to Rapture

Tags:

Jack goes to Rapture

풀이 핵심

문제에서는 처음과 마지막까지의 모든 SubTree 중
SubTree의 최댓값 중 최솟값을 요구하고 있다.

먼저, 최솟값을 요구 하고 있기 때문에, SubTree의 최솟값인 MST을
Prim’s 또는 Kruskal 알고리즘을 통해 구할 수 있고,

이 SubTree에서의 최댓값을 구하면 된다.

두 알고리즘은 모두 Greedy 알고리즘으로,
Kruskal은 최솟값의 간선을 계속 이어 붙이는 방법이고,
Prim’s는 아무데나 시작해서 현재에서의 최솟값의 간선을 이어가는 방법이다.

Algorithm

[Kruskal MST]

  1. 일단 노드를 모두 우선순위 큐에 넣는다.
  2. 순서데로 큐에서 값을 뺀다.
  3. 해당 노드에서 시작점과 끝점을 연결을 시도한다.
  4. 시작점과 끝점의 root가 같으면, 이미 연결되어 있으므로 패스한다.
  5. root가 다르다면, 서로 연결한다. (이때 최댓값을 갱신한다.)
  6. 끝점에 도달했다면, 종료한다.
  7. 2~6과정을 큐에서 데이터가 없을 때까지 반복한다.

[Prim’s MST]

  1. 아무데다 시작한다. (여기서는 1로 지정)
  2. 우선순위큐에 이미 간곳을 제외하고, 현재로부터 갈 수 있는 모든 노드를 추가한다.
  3. 우선순위 큐에서 최솟값의 간선값을 갖는 노드를 가져온다.
  4. 이전값과 비교하여 더 큰값으로 갱신한다. (최댓값 갱신)
  5. 끝점에 도달했다면, 종료한다.
  6. 그렇지 않다면, 다음 노드를 검색하며 과정 2~5를 반복한다.

Source Code

[Kruskal MST]
import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.regex.*;
import java.util.stream.*;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

class Result {

    /*
     * Complete the 'getCost' function below.
     *
     * The function accepts WEIGHTED_INTEGER_GRAPH g as parameter.
     */

    /*
     * For the weighted graph, <name>:
     *
     * 1. The number of nodes is <name>Nodes.
     * 2. The number of edges is <name>Edges.
     * 3. An edge exists between <name>From[i] and <name>To[i]. The weight of the edge is <name>Weight[i].
     *
     */
    public static class Node implements Comparable<Node>{
        int from;
        int to;
        int weight;
        Node(int from, int to, int weight){
            this.from = from;
            this.to = to; 
            this.weight = weight;
        }
        @Override
        public int compareTo(Node arg0) {
            // TODO Auto-generated method stub
            return this.weight <= arg0.weight ? -1 : 1;
        }
    }
    
    public static int find(int[] parents, int a) {
        
        if(a == parents[a]) return a;        
        
        parents[a] = find(parents, parents[a]);
        
        return parents[a];
        
    }
    
    public static void union(int[] parents, int a, int b) {
        if(a == b )return;
        
        int rootA = find(parents, a);
        int rootB = find(parents, b);
        
        parents[rootB] = rootA;
    }
    

    public static void getCost(int gNodes, List<Integer> gFrom, List<Integer> gTo, List<Integer> gWeight) {
    // Print your answer within the function and return nothing
        Iterator[] it = new Iterator[3];
        it[0] = gFrom.iterator();
        it[1] = gTo.iterator();
        it[2] = gWeight.iterator();
        
        PriorityQueue<Node> minWeightQue = new PriorityQueue<>();
        
        while(it[0].hasNext())
        {
            int from = (int)it[0].next();
            int to= (int)it[1].next();
            int weight= (int)it[2].next();
            
            minWeightQue.add(new Node(from, to , weight));            
        }
        
        int[] parents = new int[gNodes + 1];
        int[] minDistance = new int[gNodes + 1];
        for(int i = 0; i < parents.length; i++) {
            parents[i] = i;            
        }
                 
        // int index = 0;
        int max = 0;
        
        while(minWeightQue.isEmpty() == false) {
            Node temp = minWeightQue.poll();
            
            int to = temp.to;
            int from = temp.from;
            
            int rootTo = find(parents, to);
            int rootFrom = find(parents, from);
            
            if(rootTo == rootFrom) continue;
            
            union(parents, rootTo, rootFrom);
            
            // minDistance[index++] = temp.weight;
            if(temp.weight > max) max = temp.weight;
            
            if(find(parents, 1) == find(parents, gNodes)) break; 
        }
        
        
        
        if(find(parents, 1) != find(parents, gNodes)) {
            System.out.println("NO PATH EXISTS");
            return;
        }
        
        /*
        for(int i = 0; i < index ; i++)
        {
            if(minDistance[i] > max) max = minDistance[i];
        }
        */
        System.out.println(max);
        
        
        
    }

}

public class Solution {
    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));

        String[] gNodesEdges = bufferedReader.readLine().replaceAll("\\s+$", "").split(" ");

        int gNodes = Integer.parseInt(gNodesEdges[0]);
        int gEdges = Integer.parseInt(gNodesEdges[1]);

        List<Integer> gFrom = new ArrayList<>();
        List<Integer> gTo = new ArrayList<>();
        List<Integer> gWeight = new ArrayList<>();

        IntStream.range(0, gEdges).forEach(i -> {
            try {
                String[] gFromToWeight = bufferedReader.readLine().replaceAll("\\s+$", "").split(" ");

                gFrom.add(Integer.parseInt(gFromToWeight[0]));
                gTo.add(Integer.parseInt(gFromToWeight[1]));
                gWeight.add(Integer.parseInt(gFromToWeight[2]));
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        });

        Result.getCost(gNodes, gFrom, gTo, gWeight);

        bufferedReader.close();
    }
}

[prims]
import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.regex.*;
import java.util.stream.*;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

class Result {

    /*
     * Complete the 'getCost' function below.
     *
     * The function accepts WEIGHTED_INTEGER_GRAPH g as parameter.
     */

    /*
     * For the weighted graph, <name>:
     *
     * 1. The number of nodes is <name>Nodes.
     * 2. The number of edges is <name>Edges.
     * 3. An edge exists between <name>From[i] and <name>To[i]. The weight of the edge is <name>Weight[i].
     *
     */
    public static class Node implements Comparable<Node>{
        int to;
        int weight;
        Node(int to, int weight){
            this.to = to; this.weight = weight;
        }
        @Override
        public int compareTo(Node arg0) {
            // TODO Auto-generated method stub
            return this.weight <= arg0.weight ? -1 : 1;
        }
    }
    
    
    public static void getCost(int gNodes, List<Integer> gFrom, List<Integer> gTo, List<Integer> gWeight) {
    // Print your answer within the function and return nothing
        ArrayList<Node> nodes[] = new ArrayList[gNodes+1];
        for(int i = 0; i < nodes.length; i++) {
            nodes[i] = new ArrayList<>();
        }
        
        Iterator[] it = new Iterator[3];
        it[0] = gFrom.iterator();
        it[1] = gTo.iterator();
        it[2] = gWeight.iterator();
        
        while(it[0].hasNext())
        {
            int from = (int)it[0].next();
            int to= (int)it[1].next();
            int weight= (int)it[2].next();
            
            nodes[to].add(new Node(from, weight));
            nodes[from].add(new Node(to, weight));
        }
        
        Queue<Integer> haveToGo = new LinkedList<>();
        PriorityQueue<Node> minWeightQue = new PriorityQueue<>();
        haveToGo.add(1);
        int[] isMoved = new int[gNodes+1];
        isMoved[1] = -1;
        int maxWeight = 0;
        
        while(haveToGo.isEmpty() == false) {
            int cur = haveToGo.poll();
            
            for(int i = 0; i < nodes[cur].size(); i++) {
                
                Node next = nodes[cur].get(i);
                int to = next.to;
                int weight = next.weight;
                
                // 1. 안가본 곳이라면,
                if(isMoved[to] == 0) {
                    minWeightQue.add(new Node(to, weight));                    
                }
            }
            
            // 2. 최소치를 본다.            
            while(minWeightQue.isEmpty() == false) {
                Node minNode = minWeightQue.poll();
                int to = minNode.to;
                int weight = minNode.weight;
                
                // 안가본 곳이라면,                 
                if(isMoved[to] == 0) {
                    
                    int newWeight;
                    // 간선 가중치를 갱신한다 :              
                    if(isMoved[cur] == 0) {    // 최초 갱신
                        newWeight = weight;
                    } else {    // 지난것과 비교 후 최대치
                        newWeight = weight > isMoved[cur] ? weight : isMoved[cur];
                    }
                    
                    isMoved[to] = newWeight;
                    
                    if(maxWeight < newWeight) maxWeight = newWeight;
                    
                    //다음 갈 곳을 지정하고,
                    if(to == gNodes) {
                        break;
                    } else {
                        haveToGo.add(to);
                    }
                    
                    break;
                }
            }            
        }
        
        if(isMoved[gNodes] == 0) {
            System.out.println("NO PATH EXISTS");
        } else {
            System.out.println(isMoved[gNodes]);
        }
        
    }
}
public class Solution {
    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));

        String[] gNodesEdges = bufferedReader.readLine().replaceAll("\\s+$", "").split(" ");

        int gNodes = Integer.parseInt(gNodesEdges[0]);
        int gEdges = Integer.parseInt(gNodesEdges[1]);

        List<Integer> gFrom = new ArrayList<>();
        List<Integer> gTo = new ArrayList<>();
        List<Integer> gWeight = new ArrayList<>();

        IntStream.range(0, gEdges).forEach(i -> {
            try {
                String[] gFromToWeight = bufferedReader.readLine().replaceAll("\\s+$", "").split(" ");

                gFrom.add(Integer.parseInt(gFromToWeight[0]));
                gTo.add(Integer.parseInt(gFromToWeight[1]));
                gWeight.add(Integer.parseInt(gFromToWeight[2]));
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        });

        Result.getCost(gNodes, gFrom, gTo, gWeight);

        bufferedReader.close();
    }
}

Roads and Libraries

Tags:

Roads and Libraries

풀이 핵심

문제에서 요구하는 모든 도시에서 도서관을 가는 방법에는

방법 1 : 도서관 하나와 도로를 수리한다.
방법 2 : 모든 도시에 도서관을 짓는다.

각 방법의

  • 방법 1: 총 도로의 수 * 도로 수리비용 + 도서관 1개 짓는 비용
  • 방법 2: 도서관 1개를 짓는 비용 * 도시 수

둘중 더 코스트가 적은 쪽을 선택한다.

Algorithm

  1. 입력을 사용하기 편하게 가공한다.
  2. 이미 이동했던 도시가 아니라면, bfs를 통해 간선(edge)의 수를 측정한다.
  3. 방법 1 과 방법 2 중 코스트가 더 적은 쪽을 선택한다.
  4. 모든 도시의 코스트를 측정할때까지 2-3을 반복한다.

Source Code

import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.*;

public class Solution {

    static long bfs(ArrayList<Integer> nodes[], int start, boolean[] isMoved) {
        long totalWeight = 0;
        Queue<Integer> haveToMove = new LinkedList<>();
        
        haveToMove.add(start);
        isMoved[start] = true;
        
        while(haveToMove.isEmpty() == false) {
            int cur = haveToMove.poll();
            
            
            for(int i = 0; i < nodes[cur].size(); i++) {
                int next = nodes[cur].get(i);
                if(isMoved[next] == false) {                    
                    haveToMove.add(next);
                    isMoved[next] = true;
                    totalWeight++;
                }
            }
        }
        
        return totalWeight;
    }
    
    
    // Complete the roadsAndLibraries function below.
    static long roadsAndLibraries(int n, int c_lib, int c_road, int[][] cities) {
        ArrayList<Integer> nodes[] = new ArrayList[n+1];
        for(int i = 0 ; i <= n ;  i++) {
            nodes[i] = new ArrayList<>();
        }
        
        for(int i = 0; i < cities.length; i++)
        {
            int from = cities[i][0];
            int to = cities[i][1];
            
            nodes[from].add(to);
            nodes[to].add(from);
        }
        
        boolean[] isMoved = new boolean[n+1];
        
        long totalWeight = 0;
        
        for(int i = 1 ; i <= n ; i++)
        {
            if(isMoved[i] == false)
            {
                long weight = bfs(nodes, i, isMoved);
                
                long roadCost = weight * (long)c_road + (long) c_lib;
                long cityCost = (long) c_lib * (weight + 1);
                
                totalWeight += roadCost > cityCost ? cityCost : roadCost;
            }
        }
        
        
        return totalWeight;
    }

    private static final Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) throws IOException {
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));

        int q = scanner.nextInt();
        scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");

        for (int qItr = 0; qItr < q; qItr++) {
            
            int n = scanner.nextInt();
            int m = scanner.nextInt();
            int c_lib = scanner.nextInt();
            int c_road = scanner.nextInt();

            int[][] cities = new int[m][2];

            for (int i = 0; i < m; i++) {
                scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");

                for (int j = 0; j < 2; j++) {
                    int citiesItem = scanner.nextInt();
                    cities[i][j] = citiesItem;
                }
            }

            long result = roadsAndLibraries(n, c_lib, c_road, cities);

            bufferedWriter.write(String.valueOf(result));
            bufferedWriter.newLine();
        }

        bufferedWriter.close();

        scanner.close();
    }
}

Jeanie's Route

Tags:

Jeanie’s Route

풀이 핵심

  1. 갈 필요 없는 곳을 찾아 낸다.
    • letter를 배달해야 하는 지역은 당연히 가야 한다.
    • letter를 배달하지 않는 지역이라도 갈 필요가 없는 곳이 있다. (leaf node)
  2. 전체 트리의 순회 결과를 계산법을 알아내야 한다.
    • 가장 가중치가 큰 곳은 한 번만 가야 하고, 그 외에는 2번씩 갈 수밖에 없다.
    • 즉, graph의 diameter는 가장 가중치가 큰 노드의 합으로 한번만 이동하고
    • 나머지 노드는 2번씩 이동하므로,
    • total weight = edge의 합 * 2 - diameter가 된다.
  3. 어디서 시작을 해야 하는지 찾아내야 한다.
    • 계산법에 의해 가장 큰 가중치에서 시작해야 하므로,
    • 일단 bfs로 돌려보면서 가장 큰 가중치에서 시작점을 찾을 수 있다.

Algorithm

  1. 입력 데이터를 사용하기에 알맞게 가공한다.
  2. 갈 필요 없는 곳을 찾아낸다.
    • leaf node의 특징은 node의 차수가 1인 노드임을 이용한다.
    • 모든 노드에대해 검색을 한다.
    • 해당 노드가 letter 도시가 아니고 차수가 1인경우 갈 필요 없다고 표시한다.
    • 갈 필요 없는 노드는 제거 됐으므로, 연결되어 있는 노드의 차수를 1 감소 한다.
  3. bfs로 한번 돌려서 어디서 시작해야 하는 지 찾아낸다.
  4. bfs로 또 돌려서 최종 가중치의 합과 diameter로 답을 찾아낸다.

Source Code

소스 코드가 엉망.. 나중에 다시 최적화 할 것

import java.io.*;
import java.math.*;
import java.text.*;
import java.util.*;
import java.util.regex.*;


public class Solution {

    public static class Node
    {
        int to;
        int weight;
        
        Node(int to, int weight)
        {
            this.to = to;
            this.weight = weight;
        }
    }
    
    static boolean[] prune(boolean[] letterCity, ArrayList<Node> nodes[])
    {
        boolean[] doNotNeedToGo = new boolean[nodes.length];
        
        int[] degree = new int[nodes.length];
        
        Queue<Integer> checkQue = new LinkedList<>();        
        
        for(int i = 0; i < degree.length; i++)
        {
            degree[i] = nodes[i].size();
        }
        
        // 편지를 보낼 대상이 아니고, 차수(degree)의 수가 1인경우가 갈 필요 없는 곳이다.
        for(int i = 1 ; i < nodes.length ; i++)
        {
            if(letterCity[i] == true) continue;
                    
            if(degree[i] == 1) checkQue.add(i);
        }
        
        while(checkQue.isEmpty() == false)
        {
            int removeTarget = checkQue.poll();
            doNotNeedToGo[removeTarget] = true;
            
            // 주변을 살펴 본다.
            for(int i = 0 ; i < nodes[removeTarget].size() ; i++)
            {
                Node temp = nodes[removeTarget].get(i);
                int next = temp.to;
                if(letterCity[next] == true) continue;
                else if(--degree[next] == 1) 
                {
                    if(doNotNeedToGo[next] == false)
                    {
                        checkQue.add(next);
                    }
                }
            }
        }
        
        return doNotNeedToGo;
    }
    
    static int[] bfs(ArrayList<Node> nodes[], boolean[] doNotNeedToGo, int start)
    {
        int[] weightSum = new int[nodes.length];
        int[] result = new int[3];

        int bestWeightCity = 0;
        int weightTotal = 0;
        
        Queue<Integer> que = new LinkedList<>();
        
        que.add(start);
        
        for(int i = 0 ; i < weightSum.length; i++)
        {
            weightSum[i] = -1;
        }
        
        weightSum[start] = 0;
        bestWeightCity = start;
        
        while(que.isEmpty() == false)
        {
            int cur = que.poll();
            for(Node temp : nodes[cur]) {
                
                // 한번도 안간 곳이고, 갈 필요가 있는 곳이면
                int next = temp.to;
                if(weightSum[next] == -1 && doNotNeedToGo[next] == false)
                {
                    que.add(next);
                    int weight = temp.weight;
                    
                    weightTotal += weight;
                    
                    weightSum[next] = weightSum[cur] + weight;
                    
                    if(weightSum[next] > weightSum[bestWeightCity]) bestWeightCity = next;
                }
            }
        }
        
        result[0] = weightTotal;
        result[1] = weightSum[bestWeightCity];
        result[2] = bestWeightCity;
        
        return result;
    }
    
    /*
     * Complete the jeanisRoute function below.
     */
    static int jeanisRoute(int[] city, int[][] roads) {
        /*
         * Write your code here.
         */
        // 1. 도로 정보를 이용하여, 사용하기 편리한 데이터로 가공
        // 메모리가 아깝지만.. 속도 면에서는 더 빠름.
        // 최소 도로 데이터 보단 도시 수가 적음
        ArrayList<Node> nodes[] = new ArrayList[roads.length+1+1];
        for(int i = 0; i < nodes.length; i++)
        {
            nodes[i] = new ArrayList<>();
        }
        
        for(int i = 0 ; i < roads.length ; i++)
        {
            int from = roads[i][0];
            int to = roads[i][1];
            int weight = roads[i][2];
            nodes[from].add(new Node(to,weight));
            nodes[to].add(new Node(from,weight));
        }
        
        boolean[] letterCity = new boolean[nodes.length];
        
        for(int i = 0 ; i < city.length ; i++ )
        {
            letterCity[city[i]] = true;
        }
        
        // 2. 이제 돌 필요가 없는 도시를 찾는다.
        boolean[] doNotNeedToGo = prune(letterCity, nodes);
        
        // 3. 일단 bfs를 돌리면서 가장 먼곳을 찾는다.
        int result[] = bfs(nodes, doNotNeedToGo, city[0]);
        result = bfs(nodes, doNotNeedToGo, result[2]);
        
        int totalWeight = result[0];
        int diameter = result[1];
        
        return totalWeight * 2 - diameter;
    }

    private static final Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) throws IOException {
       BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));

        int n = scanner.nextInt();
        int k = scanner.nextInt();
        int[] city = new int[k];

        for(int i = 0; i < k ; i++) {
            city[i] = scanner.nextInt();
        }
        
        int[][] roads = new int[n-1][3];

        for (int roadsRowItr = 0; roadsRowItr < n-1; roadsRowItr++) {
            for(int i =0 ; i < 3 ; i++) {
                roads[roadsRowItr][i] = scanner.nextInt();
            }
        }

        int result = jeanisRoute(city, roads);
        // System.out.println(result);
       bufferedWriter.write(String.valueOf(result));
       bufferedWriter.newLine();

       bufferedWriter.close();
    }
}


Tags:

Snakes and Ladders: The Quickest Way Up

풀이 핵심

  1. “최단거리의 탐색 = BFS(Breadth First Search)” 임을 이용한다.
  2. 최단거리를 기록하는 것으로는 board에 기록하여 최단 거리를 기록한다.

Algorithm

기본적으로 BFS알고리즘을 이용한다. Queue를 이용하여 서치.

사다리와 뱀의 정보를 어떻게 이용할지는

  • 사다리와 뱀정보를 보드에 기록하여 사용 (quickestWayUp)
  • 사다리와 뱀정보를 움직일때마다 계속 검색 (quickestWayUp2)

일 수 있지만, 미리 보드에 기록하는 편이 더 좋음. 검색 횟수 ↓

  1. 사다리와 뱀 정보를 맵에 업데이트 시킨다.
    1-1. 사다리와 뱀은 -로 표기
  2. 목적지에 도착했는지 확인한다.
  3. 주사위를 굴린다. (현재부터 최대 6부터 검색 - 무조건 빨리가야 좋은거니까)
    2-1. 뱀 또는 사다리(-)라면 먼저 그곳으로 이동하고,
    2-2. 아니라면 주사위의 최댓값으로 이동한다.
  4. 각각 이동시 이동정보를 board에 업데이트 한다.

Source Code

import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.*;

public class Solution {

	static boolean isDebug = false;
	
	static final int boardGoal = 100;
	
    // Complete the quickestWayUp function below.
    static int quickestWayUp(int[][] ladders, int[][] snakes) {

    	Queue<Integer> haveToMove = new LinkedList<>();
    	int[] board = new int[boardGoal+1];    	
    	
    	int[][][] searchList = new int[2][][];
    	
    	searchList[0] = ladders;
    	searchList[1] = snakes;
    	
    	haveToMove.add(1);
    	int cur = 1;
    	
    	// 사다리 뱀 정모를 맵에 업데이트 시킨다.
		for(int j = 0 ; j < searchList.length; j++)
		{
			int[][] tempList = searchList[j];
			
			for(int i = 0 ; i < tempList.length; i++)
    		{
    			int startP = tempList[i][0];
    			int endP = tempList[i][1];
    			
    			board[startP] = -endP;
    		}
		}
    	
    	while(haveToMove.isEmpty() == false)
    	{	
    		cur = haveToMove.poll();
    		int moves = board[cur];
    		
    		if(isDebug)System.out.println();    		
    		if(isDebug)System.out.print(cur+"("+moves+")"+"->");
    		
    		if(cur == boardGoal) return board[cur];
    		else if(cur + 6 >= boardGoal) return moves+1;
    		
    		moves += 1;
    		
    		// 주사위를 굴린다.
    		boolean diced = false;
    		for(int i = 6 ; i >= 1; i--)
    		{
    			// 만약 사다리 또는 뱀이라면.. 갑시다.
    			if(board[cur+i] < 0)
    			{	    				
    				board[cur+i] = -board[cur+i];
    				haveToMove.add(board[cur+i]);
    				board[board[cur+i]] = moves;
    			} // 주사의 최대값을 구한다.
    			else if(board[cur+i] == 0 && diced == false)
    			{
    				diced = true;
    				haveToMove.add(cur+i);
    				board[cur+i] = moves;
    			}
    		}
    	}
    	
    	if(board[boardGoal] == 0) return -1;
    	    	
    	return board[boardGoal];
    }
    
    static int quickestWayUp2(int[][] ladders, int[][] snakes) {

    	Queue<Integer> haveToMove = new LinkedList<>();
    	int[] board = new int[boardGoal+1];
    	
    	int[][][] searchList = new int[2][][];
    	
    	searchList[0] = ladders;
    	searchList[1] = snakes;
    	
    	haveToMove.add(1);
    	int cur = 1;
    	
    	while(haveToMove.isEmpty() == false)
    	{	
    		cur = haveToMove.poll();
    		int moves = board[cur];
    		
    		if(isDebug)System.out.println();    		
    		if(isDebug)System.out.print(cur+"("+moves+")"+"->");
  
    		boolean[] cantMove = new boolean[6];
    				
    		int minMoves = cur + 1;
    		int maxMoves = cur + 6;
    		
    		moves += 1;
    		
    		// check end
    		if(maxMoves >= boardGoal)
    		{    			
    			board[boardGoal] = cur == boardGoal ? moves - 1: moves;
    			break;
    		}
    		
    		// check ladder & snake
    		for(int j = 0 ; j < searchList.length; j++)
    		{
    			int[][] tempList = searchList[j];
    			
    			for(int i = 0 ; i < tempList.length; i++)
	    		{
	    			int startP = tempList[i][0];
	    			int endP = tempList[i][1];
	    			if(minMoves <= startP && startP <= maxMoves)
	    			{
	    				cantMove[5-(maxMoves-startP)] = true;
	    				
	    				if(board[startP] == 0 && board[endP] == 0)
	    				{
	    					if(isDebug)System.out.print("["+startP+"->"+endP+"]");
		    				
		    				haveToMove.add(endP);
		    				board[endP] = moves;
	    				}
	    			}
	    		}
    		}
    		 
    		// add max move
    		for(int i = cantMove.length - 1 ; i >= 0 ; i--)
    		{
    			if(cantMove[i] == false)
    			{
    				haveToMove.add(i+cur+1);
    				board[i+cur+1] = moves;
    				
    				if(isDebug)System.out.print("("+(i+cur+1)+")");
    				
    				break;
    			}
    		}
    	}
    	
    	if(board[boardGoal] == 0) return -1;
    	    	
    	return board[boardGoal];
    }

    private static final Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) throws IOException {
//        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));

        int t = scanner.nextInt();
        scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");

        for (int tItr = 0; tItr < t; tItr++) {
            int n = scanner.nextInt();
            scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");

            int[][] ladders = new int[n][2];

            for (int i = 0; i < n; i++) {
                String[] laddersRowItems = scanner.nextLine().split(" ");
                scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");

                for (int j = 0; j < 2; j++) {
                    int laddersItem = Integer.parseInt(laddersRowItems[j]);
                    ladders[i][j] = laddersItem;
                }
            }

            int m = scanner.nextInt();
            scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");

            int[][] snakes = new int[m][2];

            for (int i = 0; i < m; i++) {
                String[] snakesRowItems = scanner.nextLine().split(" ");
                scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");

                for (int j = 0; j < 2; j++) {
                    int snakesItem = Integer.parseInt(snakesRowItems[j]);
                    snakes[i][j] = snakesItem;
                }
            }

            int result = quickestWayUp(ladders, snakes);

//            bufferedWriter.write(String.valueOf(result));
//            bufferedWriter.newLine();
            System.out.println(result);
        }

//        bufferedWriter.close();

        scanner.close();
    }
}


Prim's (MST) Special Subtree

Tags:

Prim’s (MST) : Special Subtree

풀이 핵심

Prim’s 알고리즘에 대해 잘 이해하고 있다.
해당 문제는 그래프에서 subtree 중 최소 가중치를 원하고 있다.
Prim’s 알고리즘을 잘 구현하면 된다.

※ MST(Minimal Spanning Tree : 최소 신장 트리) 알고리즘은 대표적으로 Kruskal MST와 Prim’s MST가 있다.

둘다 Greedy 알고리즘의 일종이지만,
Prim’s 알고리즘은 트리를 greedy하게 만들고(계속 이어 붙임),
Kruskal은 엣지를 greedy하게 Tree를 만든다(만들다가 이어 붙임).

Algorithm

노드 추가 시 최솟값을 쉽게 구하기 위한 우선순위 큐를 이용

  1. 서치를 해야 하는 지점에서 이미 이동한 곳을 제외하고 우선순위 큐에 Node를 추가한다.
  2. 이미 이동한 노드가 아니라면, 우선순위 큐에서 최솟값의 노드를 가져오고 더한다.
  3. 1과2를 더 이상 서치 할 수 없을때까지 반복한다.

Source Code

import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.*;

public class Solution {
    public static class Node implements Comparable<Node>{
        int start;
        int end;
        int weight;
        
        Node(int start, int end, int weight)
        {
            this.start = start;
            this.end = end;
            this.weight = weight;
        }

        @Override
        public int compareTo(Node arg0) {
            // TODO Auto-generated method stub
            return arg0.weight >= this.weight ? -1 : 1; 
        }
    }
    
static int find(int[] parents, int node)
    {
        int root;
        
        if(parents[node] == node) return node;
        
        parents[node] = find(parents, parents[node]); // 찾을 때 마다 성능 개선
            
        return parents[node];
    }
    
    static void union(int[] parents, int nodeA, int nodeB)
    {
        int rootA = find(parents, nodeA);
        int rootB = find(parents, nodeB);
        
        parents[rootB] = rootA;
    }
    
    
    static int kruskal(int n, int[][] edges, int start) {
        int minWeight = 0;
        
        PriorityQueue<Node> minWeightQue = new PriorityQueue<>();
        int[] parents = new int[n+1];
        
        // 우선 순위 큐에 모든 데이터를 넣어둠.
        for(int i =0 ; i < edges.length; i++) 
        {
            int from = edges[i][0];
            int to = edges[i][1];
            int weight = edges[i][2];
            
            minWeightQue.add(new Node(from,to,weight));
        }
        
        // 각 노드의 부모는 각자로 초기화
        for(int i = 0; i < parents.length; i++)
        {
            parents[i] = i;
        }
        
        // 큐가 빌때까지 탐색
        while(minWeightQue.isEmpty() == false)
        {
            // 1. 큐에서 빼옴.
            Node tempNode = minWeightQue.poll();
            
            // 2. 이제 이어야 되는데 두 노드가 이미 이어져 있는 경우는 안되니까 패스
            int rootA = find(parents, tempNode.start);
            int rootB = find(parents, tempNode.end);
            
            if(rootA == rootB) continue;
            
            // 두개 이어 주고 (root를 맞춤)
            union(parents,tempNode.start, tempNode.end);
            
            minWeight += tempNode.weight;
        }
        
        return minWeight;
    }

    // Complete the prims function below.
     static int prims(int n, int[][] edges, int start) {
        int minWeight = 0;

        ArrayList<Node> nodeLists[] = new ArrayList[n + 1];
        for(int i = 0; i < nodeLists.length; i++)
        {
            nodeLists[i] = new ArrayList<>();
        }
        
        // 탐색을 위한 리스트 생성
        for(int i =0 ; i < edges.length; i++) 
        {
            int from = edges[i][0];
            int to = edges[i][1];
            int weight = edges[i][2];
            
            nodeLists[from].add(new Node(from,to,weight));
            nodeLists[to].add(new Node(to,from,weight));
        }
        
        // 탐색 및 최솟값
        
        PriorityQueue<Node> minWeightQue = new PriorityQueue<>();    // 현재 탐색한 곳에서의 최소 값을 알아내기 위한 우선순위 큐 생성
        Queue<Integer> haveToSearch = new LinkedList<>();             // 탐색을 해야하는 곳을 위한 큐 생성. (사실.. 큐가 아니여도 괘낞아 보임)
        boolean[] isMoved = new boolean[n+1];                        // 이동완료를 알기 위한 flag
        
        // 처음 위치는 start로 지정되어 있으므로, start로
        haveToSearch.add(start);
                
        // 더이상 서치 할 곳이 없으면 종료
        while(haveToSearch.isEmpty() == false)
        {
            // 서치 해야 하는 곳과 리스트를 받아 오고
            int curNode = haveToSearch.poll();            
            ArrayList tempList = nodeLists[curNode];
            
            // 현재 노드에서 탐색을 시작, 여긴 이동 했으니 이동했다고 표시
            isMoved[curNode] = true;
            
            for(int i = 0; i < tempList.size(); i++)
            {
                Node tempNode = (Node)tempList.get(i);
                // 이미 탐색을 하지 않는 곳이라면 추가를 한다. 
                if(isMoved[tempNode.end] == false)
                {
                    minWeightQue.add(tempNode);
                }                                
            }
            
            // 탐색을 다 했다면, 현재에서의 최솟값을 받아 온다.
            while(minWeightQue.isEmpty() == false)
            {
                Node tempNode = minWeightQue.poll();
                int next = tempNode.end;
                // 이미 갔던 곳이 아니라면 ( 중복 방지) 최솟값을 추가 한다.
                if(isMoved[next] == false)
                {
                    minWeight += tempNode.weight;
                    
                    // 그리고 다음 탐색지에 여기를 추가한다.
                    haveToSearch.add(next);
                    
                    break;
                }
            }
            
        }
        
        return minWeight;
    }

    private static final Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) throws IOException {
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));

        String[] nm = scanner.nextLine().split(" ");

        int n = Integer.parseInt(nm[0]);

        int m = Integer.parseInt(nm[1]);

        int[][] edges = new int[m][3];

        for (int i = 0; i < m; i++) {
            String[] edgesRowItems = scanner.nextLine().split(" ");
            scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");

            for (int j = 0; j < 3; j++) {
                int edgesItem = Integer.parseInt(edgesRowItems[j]);
                edges[i][j] = edgesItem;
            }
        }

        int start = scanner.nextInt();
        scanner.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");

        // int result = prims(n, edges, start);
        int result = kruskal(n, edges, start);        

        bufferedWriter.write(String.valueOf(result));
        bufferedWriter.newLine();

        bufferedWriter.close();

        scanner.close();
    }
}