알고리즘/알고리즘(Java)

21. [다이나믹] 최소 공정시간 찾기

산을좋아한라쯔 2015. 4. 21. 15:36
반응형

[문제]

N개의 작업공정이 있다. 공정마다 소요되는 시간이 존재하고, 각 공정들 끼리 관계가 존재할 때는 선행 공정이 끝나야만 다음공정으로 넘어갈 수 있다. 

예를 들어 아래 공정을 보면, A공정에 10이 소요되고 난 후, B와 C가 동시에 진행이 된다. 그렇게되면 B공정이 끝나는 시점은 30이되고, C공정은 110에 끝나게 된다. 

D공정은, B와 C가 모두 끝나는 시점인(=C가 끝나는 시점) 110에 시작하게 되고, 따라서 D공정은 130에 끝나게 된다.


이러한 원칙을 적용해서, 임의로 주어지는 공정에 대해 목표되는 공정까지 소요되는 최소시간을 구하시오.



입력

첫 째줄에 문제 개수 T가 나오고, 그 다음 줄에 공정수(N)와 관계수(R)이 나온다.

그 다음 줄에 각 공정에서 소요되는 시간 N개가 나오고, 그 다음 줄 부터 공정간의 관계가 R줄에 걸쳐 나온다. 

공정간의 관계는 연결되는 공정 번호가 "앞공정번호 뒷공정번호" 순으로 나온다. 

그 다음 줄에 목표되는 공정 번호가 나온다.


예를 들어, 위에서 설명한 공정의 경우에 대한 입력은 다음과 같다.

1

4 4

10 20 100 20

1 2

1 3

2 4

3 4

4


출력

문제 한개당 한 줄씩, 최소 소요시간을 출력한다.


ex)

130


[풀이]

문제에서 예로 든 공정 그림을 아래와 같이, 각 공정마다 가장 빨리 시작할 수 있는 시간(Early Start)과 가장빨리 끝낼 수 있는 시간(Early Finish)을 손으로 표시해보자. (공정 위쪽에, 왼쪽은 ES, 오른쪽은 EF)



손으로, 위 그림의 ES, EF의 계산할 때 본능적으로 A공정에서 시작해서 오른쪽으로 가면서 계산했을 것이다.

그렇지만 각 공정의 ES를 정할 때는, 연결된 이전 공정의 EF들을 고려하면서 정해야한다. 즉, 공정D의 관점에서 보면, 앞 공정들 B,C의 EF중에서 가장 큰값이 D공정이 시작할 수 있는 ES가 된다. 

마찬가지로 B공정의 ES는, 연결된 이전공정인 A의 EF인 10이되고 EF=10+20=30이 되겠다.

C공정의 ES는 역시 A공정의 EF인 10이되고 EF=10+100=110이 되겠다.

A공정은 이전 공정이 없으므로 EF는 A공정의 소요시간인 10이 된다.


이와같이, 푸는 과정을 생각해보면, 목표되는 공정에서부터 시작해서 왼쪽방향에 연결된 공정들(선행공정들)에 대해 EF를 계산해나가다가, 선행공정이 없는 공정에서 EF=t가되는 것으로 종료를 하면 됨을 알 수 있겠다. 즉, 다이나믹 프로그래밍이 가능함을 알겠다.


다이나믹 프로그래밍에 맞게 수식을 만들어 보자.

먼저, 표현식에 대한 정의

  - Node : 각 공정을 Node라는 객체로 보자 (각 공정마다 가질 수 있는 선행공정수가 가변이어서, 객체 사용하기로 함)

  - 각 Node가 가지는 값은,

    . t : 해당 공정의 소요시간

    . ef: 해당 공정의 EarlyFinish 시간 

    . precedences: 해당 공정의 선행공정들 


수식은,

   수식1) Node[i].ef = Max{Node[i].precedences들의 ef } + Node[i].t , when Node[i].precedences.size > 0

   수식2) Node[i].ef = t , when Node[i].precedences.size == 0 

  


각 공정별로 선행공정들의 수가 가변이므로, 배열로 표현하기가 불편해서, Node라는 클래스를 만들어서 사용하자. 

이 클래스는 소요시간 t와 EarlyFinish값을 가지고, 또한 선행공정들을 가지게 된다. 이러한 클래스를 만들면 다음과 같다.


static class Node {

                  int t, earlyFinish;

                  Vector<Node> precedences = new Vector<Node>();

 

                  public Node(int t) {                       

                           this.t = t;                       

                           earlyFinish = 0;

                  }

 

                  public void addPrecedence(Node n) {

                           precedences.add(n);

                  }

         }


이 클래스에, 다이나믹 수식이 적용되는 getEF()라는 메서드를 만들자. 

public int getEF(){

                           if(precedences.size() == 0){

                                   earlyFinish = t;

                                   return earlyFinish;

                           }

                          

                           Enumeration<Node> e = precedences.elements();                         

                           Node node = null;

                           int max = 0;

                           int val = 0;                      

                           while (e.hasMoreElements()) {

                                   node = e.nextElement();

                                   if(node.earlyFinish == 0){

                                            val = node.getEF();

                                   }else{

                                            val = node.earlyFinish;

                                   }

                                   if (val > max)

                                            max = val;

                           }

                           earlyFinish=max+t;

                           return earlyFinish;

                  }

위 소스를 보면, 수식2에 해당하는 precedences가 없을 때 earlyFinish=t로 만드는 과정이 있고,

선행공정들(precedences)가 있는 경우는, 각 선행공정들의 earlyFinish중에서 가장 큰 값을 찾아내서, 현 공정의 earlyFinish값으로 치환하고 있다. 또한, 반복되는 재귀호출을 방지해서 효율화를 꾀하기 위해, earlyFinish값이 이미 계산된 공정에 대해서는 새로 계산하지 않고 사용하도록 되어 있다.


전체 내용은 아래쪽 전체소스 참조.


[소스]


문제 입력용 파일 input.txt

2

4 4

10 1 100 10

1 2

1 3

2 4

3 4

4

8 8

10 20 1 5 8 7 1 43

1 2

1 3

2 4

2 5

3 6

5 7

6 7

7 8

7



//소스


import java.io.File;

import java.util.Enumeration;

import java.util.Scanner;

import java.util.Vector;

 

public class ShortestDuration {

         // public class Main {

 

         public static void main(String[] args) throws Exception {

                  Scanner sc = new Scanner(new File("input.txt"));

                  // Scanner sc = new Scanner(System.in);

 

                  int T, N, K, W;

 

                  T = sc.nextInt();

                  for (int testNum = 0; testNum < T; testNum++) {

                           N = sc.nextInt(); // 건물개수

                           K = sc.nextInt(); // 규칙

 

                           // 건축소요시간

                           int[] t = new int[N]; // time for construction

                           for (int i = 0; i < N; i++) {

                                   t[i] = sc.nextInt();

                           }

 

                           // 규칙

                           int[][] r = new int[K][2]; // rule of schedule

                           for (int i = 0; i < K; i++) {

                                   r[i][0] = sc.nextInt();

                                   r[i][1] = sc.nextInt();

                           }

 

                           // Target

                           W = sc.nextInt();

 

                           int d = findShortestDuration(t, r, W);

                           System.out.println("" + d);

                  }

         }

 

         private static int findShortestDuration(int[] t, int[][] r, int tgt) {

                  // Node 객체 생성

                  Node[] nodes = new Node[t.length];

                  for (int i = 0; i < t.length; i++) {

                           nodes[i] = new Node(t[i]);

                  }

 

                  // 연결망 set

                  for (int i = 0; i < r.length; i++) {

                           nodes[r[i][1] - 1].addPrecedence(nodes[r[i][0] - 1]);                          

                  }

                 

                  return nodes[tgt-1].getEF();

         }

 

         static class Node {

                  int t, earlyFinish;

                  Vector<Node> precedences = new Vector<Node>();

 

                  public Node(int t) {                       

                           this.t = t;                       

                           earlyFinish = 0;

                  }

 

                  public void addPrecedence(Node n) {

                           precedences.add(n);

                  }

                                  

                  public int getEF(){

                           if(precedences.size() == 0){

                                   earlyFinish = t;

                                   return earlyFinish;

                           }

                          

                           Enumeration<Node> e = precedences.elements();                         

                           Node node = null;

                           int max = 0;

                           int val = 0;                      

                           while (e.hasMoreElements()) {

                                   node = e.nextElement();

                                   if(node.earlyFinish == 0){

                                            val = node.getEF();

                                   }else{

                                            val = node.earlyFinish;

                                   }

                                   if (val > max)

                                            max = val;

                           }

                           earlyFinish=max+t;

                           return earlyFinish;

                  }

                 

         }

 

}

 


-끝-

반응형