ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [프로그래머스]모두 0으로 만들기 - JAVA
    문제풀이/프로그래머스 2021. 6. 8. 11:31

    [프로그래머스]모두 0으로 만들기 - JAVA

    https://programmers.co.kr/learn/courses/30/lessons/76503

     

    코딩테스트 연습 - 모두 0으로 만들기

    각 점에 가중치가 부여된 트리가 주어집니다. 당신은 다음 연산을 통하여, 이 트리의 모든 점들의 가중치를 0으로 만들고자 합니다. 임의의 연결된 두 점을 골라서 한쪽은 1 증가시키고, 다른 한

    programmers.co.kr

    풀이

    많이 해메였던 문제이다. 모든 정점의 가중치를 0으로 만들려면 필요한 아이디어를 먼저 살펴보자.

     

    1. 모든 가중치의 합이 0이된다. (0이 되지 않으면 애초에 모든 정점의 가중치를 0으로 만들 수 없다)
    2. 한 방향으로 탐색을 진행하면서 가중치 연산을 해야 한다. (목적 노드를 하나 정해두고 나머지 리프 노드에서부터 목적 노드까지 가중치를 계산하며 연산 횟수를 누적시켜 한번의 탐색으로 원하는 결과를 얻을 수 있다)

     

    한 방향으로 탐색을 할때 목적 노드는 어떤 노드로 할 것이며 리프 노드는 어떻게 찾을 것인지 고민해 보았다. 리프노드를 찾아주고 순차적으로 탐색을 할 수 있도록 "위상정렬" 알고리즘을 활용하였다.

     

    • 모든 노드의 간선의 수를 저장한다. (이때 간선은 방향이 없는 무향 그래프) 
    • 간선의 수가 1개(진출만 존재)인 경우 리프노드로 간주하고 탐색 큐에 넣어준다.
    • 현재 노드와 연결된 노드로 이동하며 방문했으므로 연결 노드의 간선의 수를 줄여준다.
    • 현재 노드에서 연결된 노드로 가중치를 옮겨준다. (이때 가중치의 합은 int범위를 넘을 수 있으므로 long배열을 따로 생성해 준다.) 이 과정은 인접 노드끼리 +1, -1연산을 해 준것과 같은 의미이다.
    • 현재 노드에서 연결된 노드로 이동시킨 가중치 만큼 result에 더해준다. 
    • 현재 노드는 모든 가중치 값을 연결 노드로 이동시켰으므로 가중치 값을 0으로 변경해준다.
    • 연결 노드의 간선의 수가 1이라면 리프노드가 된 것이므로 탐색 큐에 담아준다.

    위 연산의 일련 과정을 예제 경우일때 그림으로 표현하면 다음과 같다.

     

    코드

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    import java.util.*;
     
    class Solution {
        
        ArrayList<Integer>[] list; //간선 정보로 list생성
        boolean[] visited;
        int[] indegree; //진입 간선
        long[] long_a; //연산 수행 위한 long배열
        long result; 
        
        public long solution(int[] a, int[][] edges) {
            long_a = new long[a.length];
            list = new ArrayList[a.length];
            long sum = 0
            for(int i = 0; i < a.length; i++) {
                sum += a[i];
                long_a[i] = a[i];
                list[i] = new ArrayList<>();
            }
            if(sum != 0return -1;
            
            indegree = new int[a.length];
            for(int i = 0; i < edges.length; i++) {
                list[edges[i][0]].add(edges[i][1]);
                list[edges[i][1]].add(edges[i][0]);
                indegree[edges[i][0]]++;
                indegree[edges[i][1]]++;
            }
            
            visited = new boolean[a.length];
            topology(); //위상정렬 알고리즘 활용하여 리프노드에서 루트까지 가중치 이동횟수 구함
            return result;
        }
        
        public void topology() {
            Queue<Integer> q = new LinkedList<>();
            for(int i = 0; i < indegree.length; i++) {
                if(indegree[i] == 1) q.offer(i);
            } 
            
            while(!q.isEmpty()) {
                int current = q.poll();
                visited[current] = true;
                
                for(int i = 0; i < list[current].size(); i++) {
                    int next = list[current].get(i);
                    if(!visited[next]) {
                        indegree[next]--;
                        long_a[next] += long_a[current];// +1, -1해주었다고 생각하여 합친 값을 저장    
                        result += Math.abs(long_a[current]); // 현재 노드에서 이동한 값 만큼 result에 더해줌 
                        long_a[current] = 0// 현재 노드의 모든 가중치 값을 부모로 이동.
                        if(indegree[next] == 1) q.offer(next); 
                    }
                }
            }
        }
    }
    cs

    댓글

Designed by Tistory.