Algorithm/Greedy

BOJ#6195 Fence Repair

밤이2209 2017. 2. 2. 22:59

BOJ#6195 Fence Repair


* 문제

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


(Olympiad USA Computing Olympiad 2006-2007 Season USACO November 2006 Contest Gold 1번)



* 풀이


최초 생각한 알고리즘은 가장 긴 널빤지를 가장 빨리 만드는 것이었습니다.

(늦게 처리할 수록 큰 값이 Cost에 포함되기 때문에)


그러나, 위 알고리즘의 반례는 아래와 같습니다.


ex) 5 / 1, 2, 3, 4, 5 일 때


<처음 생각한 알고리즘>

1단계 : 15 -> 5, 10 (15)

2단계 : 10 -> 4, 6 (10)

3단계 : 6 -> 3, 3 (6)

4단계 : 3 -> 21 (3)


Cost : 34



<반례>

1단계 : 15 -> 7, 8 (15)

2단계 : 7 -> 34 (7)

2단계 : 8 -> 5, 3 (8)

3단계 : 3 -> 21 (3)


Cost : 33


---------------------------------------

두 경우 Cost의 차이는 1입니다.


반례에서 처음 생각한 알고리즘과 비교했을 때,


5가 1단계가 아니라 2단계에 나오는 대신(+5)

3이 3단계가 아닌 2단계에서 나오고 (-3)

2와 1이 4단계가 아닌 3단계에서 나오기 때문입니다. (-2 -1)


 : +5 - 3 - 2 - 1 = -1


따라서 무조건 큰 널빤지를 가장 빨리 만드는 것이 최적의 해가 아님을 알게 되었습니다.


한편, 


널빤지는 항상 2개의 널빤지로 나누어지므로

널빤지를 자르는 것은 이진 트리에 대응할 수 있는데


여러가지 예에서 관찰해 보았을 때

가장 작은 널빤지와 그 다음으로 작은 널빤지는 최대 depth에서 발견되고

또한, 그 둘은 형제 노드임을 알 수 있습니다.


즉, 정리하자면

최적의 해로 널빤지를 자르는 것은 아래의 성질을 갖고 있음을 알 수 있습니다.

"가장 작은 널빤지와 그 다음으로 작은 널빤지는 가장 깊은 depth에서 형제 노드 관계이다."


따라서 위 성질을 이용하여 가장 작은 널빤지 2개를 조립하면서 최초 하나의 널빤지가 될 때까지 합쳐간다면 필요한 cost를 구할 수 있을 것입니다.



* 나의 코드

https://github.com/stack07142/BOJ/tree/master/BOJ%236195_FenceRepair/src


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* BOJ#6195 Fence Repair
* https://www.acmicpc.net/problem/6195
*/

public class Main {

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

int N; // number of planks
int[] L; // length of plank
int minIdx1, minIdx2;
long cost = 0;

// input
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

N = Integer.parseInt(br.readLine());
L = new int[N];

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

L[i] = Integer.parseInt(br.readLine());
}

// 널빤지 N 1개가 될 때까지
while (N > 1) {

minIdx1 = 0; // 가장 작은 널빤지 idx
minIdx2 = 1; // 그 다음으로 작은 널빤지 idx

if (L[minIdx1] > L[minIdx2]) {

// swap
minIdx1 ^= minIdx2;
minIdx2 ^= minIdx1;
minIdx1 ^= minIdx2;
}

// 가장 짧은 널빤지와 그 다음으로 작은 널빤지를 구한다
for (int i = 2; i < N; i++) {

if (L[i] < L[minIdx1]) {

minIdx2 = minIdx1;
minIdx1 = i;
} else if (L[i] < L[minIdx2]) {

minIdx2 = i;
}
}

// 구한 2개의 널빤지를 합친다. cost 갱신.
int subCost = L[minIdx1] + L[minIdx2];
cost += subCost;

// 널빤지 2개를 합쳤으므로 N = N-1을 해준다.
// 합친 널빤지는 L[minIdx1]에 넣어주고, 필요 없어진 minIdx2에는 L[N-1] 값을 넣어준다.
if (minIdx1 == N - 1) {

// swap
minIdx1 ^= minIdx2;
minIdx2 ^= minIdx1;
minIdx1 ^= minIdx2;
}

L[minIdx1] = subCost;
L[minIdx2] = L[N - 1];

N--;
} // while loop

System.out.println(cost);
}
}


'Algorithm > Greedy' 카테고리의 다른 글

BOJ#1541 잃어버린 괄호  (0) 2017.04.30
BOJ#1946 신입 사원  (0) 2017.03.22
BOJ#7676 Saruman's Army  (0) 2017.02.02
POJ#3617 Best Cow Line  (0) 2017.02.01
BOJ#1700 멀티탭 스케쥴링  (0) 2017.02.01