본문 바로가기

알고리즘/BOJ

[JAVA] BOJ(백준) - 생태학 - 4358

문제 내용

https://www.acmicpc.net/problem/4358

 

4358번: 생태학

프로그램은 여러 줄로 이루어져 있으며, 한 줄에 하나의 나무 종 이름이 주어진다. 어떤 종 이름도 30글자를 넘지 않으며, 입력에는 최대 10,000개의 종이 주어지고 최대 1,000,000그루의 나무가 주어

www.acmicpc.net

생태학에서 나무의 분포도를 측정하는 것은 중요하다. 그러므로 당신은 미국 전역의 나무들이 주어졌을 때, 각 종이 전체에서 몇 %를 차지하는지 구하는 프로그램을 만들어야 한다.

입력

프로그램은 여러 줄로 이루어져 있으며, 한 줄에 하나의 나무 종 이름이 주어진다. 어떤 종 이름도 30글자를 넘지 않으며, 입력에는 최대 10,000개의 종이 주어지고 최대 1,000,000그루의 나무가 주어진다.

출력

주어진 각 종의 이름을 사전순으로 출력하고, 그 종이 차지하는 비율을 백분율로 소수점 4째자리까지 반올림해 함께 출력한다.


문제 접근 방법

입력 받는 나무는 중복이 될수 있다. 그리고 우리가 필요한건 "A"라는 나무가 몇개가 있고 "A" 라는 나무가 총 나무갯수에서 몇 퍼센트의 비율을 차지하는가 이다.

 

따라서

각 나무마다 몇개를 입력받았는지 알려줄 HashMap,

입력받은 나무의 총개수 total

두가지를 이용해 풀어 낼 수 있다.

 

HashMap을 사용하는 이유는 나무이름을 key, key에 해당하는 나무 갯수 value로 하여 입력받은 나무 이름이 map에 key로 존재 할경우 value값을 증가시키고, 아닐경우 map에 넣어주기 위함이다.

 

이때 본인의 경우 HashMap의 메소드중 containsKey를 이용해 map에 나무이름이 있는지 없는지 확인후 value값을 갱신 했지만 getOrDefault 메서드를 사용해 풀어도 된다.

 

getOrDefault는 현재 탐색중인 key가 map에 있으면 그 key에 해당하는 값을 반환하고, 아니라면 내가 설정한 디폴트값이 반환된다.

따라서 getOrDefault를 사용하려면 map.put(key, getOrDefault(key, 1)) 을 이용하면 된다.

 

마지막으로 소수점을 표시할때 주의할 점이 있는데 반올림한 소수점을 표기할때 두가지 방법이 있다.

  • Math.round
    round의 경우 소수점 n자리까지 표기할때 Math.round(값*n)/n.0 을 사용다다.
  • String.format
    format의 경우 소수점 n자리까지 표기할때 String.format("%.nf", 값) 을 사용한다.
  • 차이점
    두 방법의 차이점은 만약 100.00이라는 값이 나왔을때 표기하려는 소숫점 자리가 0이라면
    round의 경우 100.00 대신 100 을 나타내게 되고,
    format의 경우 100.00 을 나타낼 수 있습니다.

차이점을 보면알겠지만 round를 사용할경우 0.00은 표시되지 않기때문에 꼭 String.format을 사용해야한다.

 

그럼 풀이를 보자.


풀이

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;

/**
 * 생태학 - 자료구조
 * 실1
 * @author USER
 *
 */
public class Main {
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		HashMap<String, Double> map = new HashMap<String, Double>(); //나무 넣을 맵
		double total = 0; //전체 나무 개수
		
		while(true) {
			String tree = br.readLine();
			if(tree == null || tree.equals("")) break; //입력값이 이럴때 while문 탈출
			
			total++;//한번 입력할때마다 total 증가
			
			//입력한 나무이름이 map에 있다면 해당 나무의 value값을 증가시키고 갱신
			//없다면 걍 넣기.
			if(map.containsKey(tree)) {
				double value = map.get(tree)+1.0;
				map.put(tree, value);
			}else {
				map.put(tree, 1.0);
			}
		}
		
		//나무이름 오름차순 정렬(key값 기준 정렬) 
		Object[] arr = map.keySet().toArray();
		Arrays.sort(arr);
		
		//나무 분포도 계산-> calTree(전체 나무개수, 나무 저장된 map, 나무이름 정렬된 배열)
		String answer = calTree(total, map, arr);
		System.out.println(answer);
	}
	
	//나무 분포도 계산
	public static String calTree(double total, HashMap<String, Double> map, Object[] arr) {
		StringBuilder sb = new StringBuilder();
		
		//순서가 가장 빠른 나무 ~ 마지막 나무까지 분포도 구하고
		//소숫점 4째자리까지 표시한거 sb에 삽입.
		for(int i=0; i<arr.length; i++) {
			String key = (String) arr[i]; //오름차순된 나무
			double per = (map.get(key)/total)*100; //해당 나무 분포도 
			String value = String.format("%.4f", per); //소숫점 4째자까지 반올림후 표기 
			
			sb.append(key).append(" ").append(value).append("\n");
		}
		
		return sb.toString();
	}
}

마치며

getOrDefault를 오랜만에 다시 공부하고 round와 foramt의 차이점을 알수 있었던 문제이다.