본문 바로가기

알고리즘/프로그래머스

[JAVA] 프로그래머스 - 전력망을 둘로 나누기

문제내용

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

 

코딩테스트 연습 - 전력망을 둘로 나누기

9 [[1,3],[2,3],[3,4],[4,5],[4,6],[4,7],[7,8],[7,9]] 3 7 [[1,2],[2,7],[3,7],[3,4],[4,5],[6,7]] 1

programmers.co.kr

n개의 송전탑이 전선을 통해 하나의 트리 형태로 연결되어 있습니다. 당신은 이 전선들 중 하나를 끊어서 현재의 전력망 네트워크를 2개로 분할하려고 합니다. 이때, 두 전력망이 갖게 되는 송전탑의 개수를 최대한 비슷하게 맞추고자 합니다.

송전탑의 개수 n, 그리고 전선 정보 wires가 매개변수로 주어집니다. 전선들 중 하나를 끊어서 송전탑 개수가 가능한 비슷하도록 두 전력망으로 나누었을 때, 두 전력망이 가지고 있는 송전탑 개수의 차이(절대값)를 return 하도록 solution 함수를 완성해주세요.


제한사항

  • n은 2 이상 100 이하인 자연수입니다.
  • wires는 길이가 n-1인 정수형 2차원 배열입니다.
    • wires의 각 원소는 [v1, v2] 2개의 자연수로 이루어져 있으며, 이는 전력망의 v1번 송전탑과 v2번 송전탑이 전선으로 연결되어 있다는 것을 의미합니다.
    • 1 ≤ v1 < v2 ≤ n 입니다.
    • 전력망 네트워크가 하나의 트리 형태가 아닌 경우는 입력으로 주어지지 않습니다.

문제풀이

문제를 보고 떠오른 생각은 이전에 비슷한 문제를 코테에서 풀어본 경험이 있는데 그때는 BFS로 풀었고, Union-Find로 푸는 풀이도 있다고 했던 기억이 떠올랐다.

그래서 이번 풀이는 Union-Find로 풀어봤다.

 

로직

1. 주어진 선중에서 선하나 제거.

2. 나머지 노드들을 연결시키는 작업을 하고, 부모노드를 정할땐 숫자가 작은걸 부모노드로 통일한다.(Union-Find)

3. 만들어진 parents배열을 가지고 노드 개수의 차이를 구한다.

4. 최솟값을 갱신한다.

3. 1~4번을 반복하면된다.(반복기준은 주어진 wires 요소값을 0번요소 제거 -> 1번요소 제거 -> 2번요소 제거 -> ....)

 

참고

노드 개수차이를 구할땐 1번을 부모로 가진 친구들의 개수부터 구한다.

이렇게 할수 있는 이유 -> 2번에서 숫자가 작은값을 부모노드로 통일 -> 최소하나의 트리는 루트노드가 1이 된다.

 


코드

public class Solution {
	static private int[] parents;//인덱스:자식, 요소:부모
    public static int solution(int n, int[][] wires) {
        int answer = Integer.MAX_VALUE;
        
        for(int i=0; i<wires.length; i++) {
        	parents = new int[n+1];//초기화
        	for(int p=0; p<=n; p++) 
        		parents[p] = p;
        	
        	for(int j=0; j<wires.length; j++) {
        		if(i==j) continue;//선하나 끊어보기
        		int v1 = wires[j][0], v2 = wires[j][1];
        		
        		//트리합치기
        		union(v1,v2);
        	}
        	
        	//나눠진 트리 차이값 확인
        	int temp = checkTree(n);
        	//최소값
        	answer = Math.min(answer, temp);
        }
        
        return answer;
    }
    
    //합치기
    public static void union(int v1, int v2) {
    	v1 = find(v1);//v1의 부모노드 찾아서 할당.
    	v2 = find(v2);//v2의 부모노드 찾아서 할당.
    	
    	//부모노드가 다르면 합칠때 부모노드의 숫자가 더작은걸로 합치기.
    	if(v1!=v2) { 
    		if(v1>v2) parents[v1] = v2;
    		else parents[v2] = v1;
    	}
    }
    
    public static int find(int v) {
    	if(parents[v]==v) return v;//자식==부모 일때 반환.
    	return parents[v] = find(parents[v]);//현재 v와 연결될 루트노드 찾아서 반환
    }
    
    //차이값 확인
    public static int checkTree(int n) {
    	//a:1을 부모노드로하는 친구들 개수
    	//b:1이 아닌 수를 부모로하는 친구들 개수
    	int a=0, b=0;
    	for(int i=1; i<=n; i++) 
    		//i번째 친구의 부모노드를 찾았는데 1이면 a증가
    		if(find(parents[i])==1) a++;
    	
    	b = n-a;//전체노드개수 - a 를 하면 분리된 트리의 노드 개수가 나옴.
    	return Math.abs(a-b);
    }
}

마치며

이번엔 Union-Find로 풀어내긴 했는데 개수를 구하는 부분에서 처음엔 if(parents[i]==1) 을 조건으로 줬는데 틀렸었다. 이후 정확히 부모노드를 찾을수 있는 find 함수를 이용하니 맞았는데, parents[i]==1로 조건을 주면 안되는 부분에 대해 한번다시 생각해볼 필요가 있다.