본문 바로가기

Dot Algo∙ DS/PS

[BOJ] 백준 17073번 나무 위의 빗물 (Java)

    #17073 나무 위의 빗물

    난이도 : 골드 5

    유형 : 트리

     

    17073번: 나무 위의 빗물

    첫째 줄에 트리의 노드의 수 N과 1번 노드에 고인 물의 양을 의미하는 정수 W가 주어진다. (2 ≤ N ≤ 500,000, 1 ≤ W ≤ 109) 다음 N-1줄에 걸쳐, 트리에 존재하는 간선의 정보가 U V의 형태로 주어진다

    www.acmicpc.net

    ▸ 문제

    트리란, 사이클이 없는 연결 그래프를 의미한다. 위 그림은 1번 정점을 루트로 하는 어떤 트리를 나타낸 모습이다.

    사실 이 트리는 영훈이가 뒷마당에서 기르고 있는 나무이다. 어제는 비가 왔기 때문에, 트리의 1번 정점에는 W만큼의 물이 고여 있다. 1번 정점을 제외한 모든 정점에는 아직 물이 고여 있지 않은 상태이다.

    이제 매초마다 모든 정점은 아래의 작업을 순서대로 반복한다.

    • 물을 가지고 있으며, 자식 정점이 있다면 자식 정점 중 하나를 골라 물을 1 준다. 자식 정점이 여러 개라면 동일한 확률로 그 중 하나를 고른다.
    • 만약 부모 정점이 자신에게 물을 흘려보냈다면 받아서 쌓아 둔다.

    이때, 위 작업은 순서대로 진행되므로 부모 정점에게 받은 물을 즉시 자식 정점에게 줄 수는 없다.

    영훈이는 나무를 바라보면서 더 이상 물이 움직이지 않는 상태가 되었을 때 각 정점에 어느 정도의 물이 있게 될지 궁금해졌다. 더 이상 물이 움직이지 않을 때, i번 정점에 쌓인 물의 양의 기댓값을 Pi라 하자. 이때, Pi가 0보다 큰 정점들에 대해서 Pi들의 평균은 어느 정도가 될까?

     입력

    첫째 줄에 트리의 노드의 수 N과 1번 노드에 고인 물의 양을 의미하는 정수 W가 주어진다. (2 ≤ N ≤ 500,000, 1 ≤ W ≤ 109)

    다음 N-1줄에 걸쳐, 트리에 존재하는 간선의 정보가 U V의 형태로 주어진다. (1 ≤ U, V ≤ N​​​​, U ≠ V)

    이는 양 끝 정점이 각각 U와 V인 간선이 트리에 존재한다는 의미이다.

    입력으로 주어지는 트리는 항상 올바른 연결 트리임이 보장되며, 주어지는 트리의 루트는 항상 1번 정점이다.

     출력

    문제의 정답을 출력한다. 정답과의 차이가 10-3 이하인 값은 모두 정답으로 인정된다.

     

    문제 풀이  

    자식 정점이라면 동일한 확률로 나눠주기 때문에 모든 리프 노드의 갯수를 구해주면 된다. 중간 정점은 고려하지 않아도 된다. 어차피 1번 루트노드에 있는 모든 물은 각 자식노드에 골고루 배분될 것이기 때문이다. 예제 트리로 따지면 다음과 같이 배분된다. 

     

    2번,3번 노드에 50%확률로 나눠지고, 3번노드에서 다시 4,5번 노드로 50%확률로 나눠지기 때문에 각 리프노드(2,4,5)의 기댓값은 10, 5, 5이다. 이를 3으로 나누면 6.6666666....이 됨을 알 수 있다.

    예시

     

    결국 Pi가 0보다 큰 정점은 리프노드들 뿐이고, 모든 리프노드 기댓값은 처음에 1번 루트노드에 들어있던 w임을 알 수 있다. 이를 평균으로 구해보면 w/(리프노드의 갯수)이다.

     

    리프노드의 기댓값은 w이다.

     

    설계

    1. 트리의 데이터를 인접리스트 배열에 저장한다.
    2. 리프노드(간선이 1개인 노드)의 갯수를 카운트해준다.
    3. Pi의 평균 = w/(리프노드 갯수)를 구해서 출력한다.

     

    풀이 코드 

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.StringTokenizer;
    
    public class Main {
    
    	static int leafN =0;
    	static List<Integer>[] list;
    	public static void main(String[] args) throws IOException{
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		StringTokenizer st = new StringTokenizer(br.readLine());
    		int n = Integer.parseInt(st.nextToken());
    		int w = Integer.parseInt(st.nextToken());
    		
    		list = new ArrayList[n+1];
    		for(int i=1; i<n+1; i++) {
    			list[i] = new ArrayList<>();
    		}
    		for(int i=0; i<n-1; i++) {
    			st = new StringTokenizer(br.readLine());
    			int a = Integer.parseInt(st.nextToken());
    			int b = Integer.parseInt(st.nextToken());
    			
    			list[a].add(b);
    			list[b].add(a);
    		}
    		
    		for(int i=2; i<n+1; i++) {
    			if(list[i].size()==1) leafN++;
    		}
    		System.out.println(String.format("%.10f", (double)w/leafN));
    	}
    }