알고리즘/백준

[백준] 1516 게임 개발 - JAVA

체제 2023. 5. 11. 20:06

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

 

1516번: 게임 개발

첫째 줄에 건물의 종류 수 N(1 ≤ N ≤ 500)이 주어진다. 다음 N개의 줄에는 각 건물을 짓는데 걸리는 시간과 그 건물을 짓기 위해 먼저 지어져야 하는 건물들의 번호가 주어진다. 건물의 번호는 1부

www.acmicpc.net

 

 

알고리즘

  • "어떤 건물을 짓기 위해서 다른 건물을 먼저 지어야 할 수도 있기 때문에"
  • 순서가 정해져 있는 작업을 차례로 수행할 때 순서를 결정하기 위한 알고리즘 : 위상정렬을 사용하여 접근 
  • 단, 여러개의 건물이 동시에 지어질 수 있는 경우를 고려하여 시간을 계산해야 한다. 

 

💡예를 들어, C 건물을 짓기 위해서는 A와 B 건물이 지어져야 한다고 가정해보자. 

A 건물이 10초, B 건물이 20초 걸린다면 C 건물은 20초 후에 지을 수 있다. A,B는 동시에 건설할 수 있기 때문에 더 긴 시간인 20초를 기다려야 한다. 


 

구현 과정

  • List<List> list : 그래프 구현을 위한 인접리스트 
  • inCnt[] : 각 건물을 짓기 전에 먼저 지어야 하는 건물의 갯수 
  • time[] : 각 건물을 짓는데 걸리는 시간 
  • pre_time[] : 각 건물을 지을 수 있을 때 까지 걸리는 시간 
  • 즉, 건물을 완성하는데 필요한 총 시간 = pre_time[i] + time[i] 

 

1. 주어진 입력정보를 이용하여 list, inCnt, time 값을 채우고 만약 inCnt[i] = 0 즉, 먼저 지어야 할 건물이 없는 경우 큐에 넣는다. 

 

2. 큐에서 하나씩 꺼내며 큐가 빌 때까지 아래 과정을 반복한다. 

 

3. 현재 건물과 그래프 상 연결되어 있는 건물 (= 현재 건물을 짓고 나면 지을 수 있는 다음 건물(들))의 inCnt를 1 줄인다. 

 

4. 현재 건물을 짓기까지 걸린 시간 + 현재 건물을 짓는데 걸린 시간 vs 다음 건물을 짓기 전까지 걸린 시간 중 더 큰 값을 pre_time 에 저장한다. 

 

5. 만약 다음 건물의 inCnt 값이 0이라면 즉, 먼저 지어야 하는 건물을 다 지었다면, 큐에 넣는다. 

 

6. 최종적으로 모든 건물들의 pre_time 과 time 값을 더하여 출력한다. 

 

 

👉예제 입력 1

 

건물번호 1 2 3 4 5
list { 2,3,4 }    { 4,5 }    
time 10 10 4 4 3
inCnt 0 1 1 2 1
pre_time 0 0 0 0 0
que 1        
  • 1 이후에 지을 수 있는 건물 { 2,3,4 } 의 inCnt 값을 1씩 줄인다. 
  • pre_time 값이 현재 0으로 초기화 되어 있으므로 pre_time 값을 time[1] = 10으로 바꾼다.
    즉, 현재까지 2,3,4 를 짓기 전까지 걸리는 시간 = 1을 짓는데 걸리는 시간 = 10 
  • inCnt 가 0이 된 2,3 건물을 큐에 넣는다.

 

 

건물번호 1 2 3 4 5
list { 2,3,4 }    { 4,5 }     
time 10 10 4 4 3
inCnt 0 0 0 1 1
pre_time 0 10 10 10 0
que 2 3      
  • que.poll() = 2, 2와 연결된 건물이 없기 때문에 다음 
  • que.poll() = 3 
  • 3 이후에 지을 수 있는 건물 { 4,5 } 의 inCnt 값을 1씩 줄인다. 
  • 현재 4번 건물을 짓기 전까지 걸리는 시간은 10 (=1번 건물만 완성) 으로 되어 있다. 
    하지만, 4번 건물을 짓기 위해선 1, 3 건물을 모두 지어야 한다. 3번 건물을 짓는데 걸리는 시간은 ( =1번 건물 완성 이후, 3번 건물 완성) ➡️ 10 + 4 = 14이다. 
    14와 10중 더 큰값인 14만큼 기다려야 4번 건물을 지을 수 있다. (만약 10만 기다렸다면 3번 건물은 다 완성하지 못하고 4번을 지은 것이기 때문에 ❌) 
  • 5번 건물 또한 3번 건물이 완성될 때 까지 기다려야 하므로 10+4 = 14 로 pre_time 값을 변경한다. 
  • 4,5 모두 isCnt 값이 0이 되었으므로 큐에 넣는다. 

 

 

건물번호 1 2 3 4 5
list { 2,3,4 }    { 4,5 }     
time 10 10 4 4 3
inCnt 0 0 0 0 0
pre_time 0 10 10 14 14
que 4 5      
  • que.poll 하여 4 , 5 를 꺼냈지만 모두 연결되어 있는 건물이 없기 때문에 종료 
  • 최종적으로 건물을 완성하는데 걸리는 시간은 건물 짓기 전까지 기다린 시간 + 건물 짓는 시간으로 pre_time 과 time 을 더하여 출력하면 끝 ! 

 

소스 코드 

import java.util.*;
import java.io.*;

public class Solution_BaekJoon_1516 {
	public static void main(String[] args) throws Exception{
		BufferedReader br= new BufferedReader(new InputStreamReader(System.in)); 
		StringTokenizer st; 
        
		Queue<Integer> que= new LinkedList<>();
		int N = Integer.parseInt(br.readLine()); //건물의 종류 수 
		List<ArrayList<Integer>> list= new ArrayList<>(); 
		for(int i=0; i<=N; i++) {
			list.add(new ArrayList<>());
		}
		
		int[] time = new int[N+1];//건물을 짓는데 걸리는 시간 
		int[] inCnt = new int[N+1];//진입차수 
        
		//입력받은 값으로 연결 리스트 그래프 구현 
		for(int i=1; i<=N; i++) {
			st= new StringTokenizer(br.readLine(), " ");
			time[i] = Integer.parseInt(st.nextToken());
			while(true) {
				int j = Integer.parseInt(st.nextToken()); 
				if(j == -1) break; 
				inCnt[i]++; 
				list.get(j).add(i); 
			}
			if(inCnt[i]==0) {
				que.add(i);
			}
		}
		
		int pre_time[] = new int[N+1]; //건물을 지을 수 있을 때까지 걸리는 시간 
		while(!que.isEmpty()) {
			int now= que.poll();
			for(int i=0; i<list.get(now).size(); i++) {
				int next= list.get(now).get(i);
				inCnt[next]--;
				pre_time[next] = Math.max(pre_time[next], time[now]+pre_time[now]); 
				if(inCnt[next] == 0) {
					que.add(next);
				}
			}
		}
		
		for(int i=1; i<=N; i++) System.out.println(time[i]+pre_time[i]);
        
	}//end of main 
}