[JS] 전력망을 둘로 나누기 - bfs에서 약간만 응용해보자!

2022. 8. 20. 17:02알고리즘/programmers

간만에 레벨2 문제네요.완전 탐색 문제입니다. 제가 푼 방법이 약간 비효율적인 것 같아서 "이사람은 이렇게 했구나"만 보셔도 충분할 것 같습니다.그럼 같이 볼까요?


문제 요약

 

[4-7] 또는 [3-4] 간선을 끊는게 이 그래프에서 두 전력망을 비슷하게 나누는 방법입니다.

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

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

 

제한사항

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

 

테스트케이스

n wires result
9 [[1,3],[2,3],[3,4],[4,5],[4,6],[4,7],[7,8],[7,9]] 3
4 [[1,2],[2,3],[3,4]] 0
7 [[1,2],[2,7],[3,7],[3,4],[4,5],[6,7]] 1

 

접근 방법

예전에 프로그래머스에서 푼 문제 중에 권투 선수 문제가 있었는데요.

이 때 썼던 개념 중에 [node1, node2]구조에서 꼬리 물기식으로 계속 내려가는 알고리즘을 썼었습니다.

이번에도 비슷한 걸 써보았습니다.

 

[JS] 순위 - 권투 선수 왜싸워요... 싸움 멈춰 제발

아니 왜 경기 결과 잃어버리고 난리임? 일단 이 문제는 레벨 3 문제 답게 풀이를 위해선 약간의 아이디어가 필요합니다. 그리고 효율적으로 풀 수 있는 약간의 아이디어도 필요하죠. 아무튼 같이

dev-russel.tistory.com

 

1. wires에서 요소 하나씩만 뺀 배열(rest라고 하겠습니다.)을 만듭니다.
([1, 3]을 뺀 배열 부터 [7, 9]를 뺀 배열까지요.)

2. rest의 첫 요소를 queue에 넣고 loop를 돌립니다.

3. dequqe 한 element와 겹치는 모든 간선을 찾습니다.
([2, 3]이 element라면 2가 있는 element, 3이 있는 element를 다 묶어줍니다.)

3. visited[rest[0]]과 visited[rest[1]]을 true로 바꿉니다.

4. 찾아낸 요소가 전에 방문한 간선이 아니라면 queue에 넣고 계속 반복합니다.

정답 코드

class Queue {
    constructor() {
        this.queue = [];
        this.front = 0;
        this.rear = 0;
    }
    
    enqueue(val) {
        this.queue[this.rear++] = val;
    }
    
    dequeue() {
        const val = this.queue[this.front];
        delete this.queue[this.front++];
        return val;
    }
    
    size() {
        return this.rear - this.front;
    }
    
    isEmpty() {
        return this.rear === this.front;
    }
}

function solution(n, wires) {
    const result = [];
    
    wires.forEach((wire, i, arr) => {
        const rest = arr.filter((v, index) => index !== i);
        // const connected = new Set();
        const visited = Array(n + 1).fill(false);
        visited[0] = true;
        const queue = new Queue();
        queue.enqueue(rest[0]);
        
        while (!queue.isEmpty()) {
            const item = queue.dequeue();
            visited[item[0]] = true;
            visited[item[1]] = true;
            
            const related = rest.filter(v => v.includes(item[0]) || v.includes(item[1]));
            related.forEach(v => {
                if (!visited[v[0]] || !visited[v[1]]) {
                    queue.enqueue(v);
                }
            });
            
        }
        result.push(Math.abs(2 * (visited.filter(v => v).length - 1) - n));
    });
    return Math.min(...result);
}

실행 결과

 

팁 1. 꼬리 물기를 써도 되는 이유

  • 전력망 네트워크가 하나의 트리 형태가 아닌 경우는 입력으로 주어지지 않습니다.

이 조건을 통해 모든 간선은 하나의 루프로 이어진다는 사실을 알 수 있습니다.

그렇기 때문에 rest 배열은 두 개의 루프로 나눠진다는 사실이 보장됩니다.

 

한 루프의 길이(k)를 구한다면 노드 수(n)를 기준으로 (n - k) 루프와 k 루프로 나눠집니다.

이 두 길이의 절댓값의 차는 (2k - n)으로 구할 수 있겠네요.

 

팁 2. 큐 대신 스택을 써도 되나요?

네 전혀 상관없습니다.

 


레벨 3에서 놀다가 레벨 2 오니까 너무 쉽네용..

간간히 레벨 2도 들려야겠습니당

 

그럼 이만!