📂문제 설명
n개의 정점으로 이루어진 트리가 있다. 이 트리의 각 정점을 색칠하려고 한다. 색칠을 할 때에는 1, 2, 3, …, n번 색깔 중에 하나로 색칠하여야 한다. 각 색깔을 사용하여 한 개의 정점을 색칠할 때마다 1, 2, …, n의 비용이 든다. 즉, i번 색깔로 한 개의 정점을 색칠하면 i만큼의 비용이 든다는 것이다.
또한 정점에 색칠을 할 때에, 주어진 트리 상에서 인접해 있는 서로 다른 두 정점은 서로 다른 색깔로 칠해져야 한다. 이를 만족하면서, 전체 정점들을 색칠하는데 드는 총 비용을 최소화 하려 한다. 최소 비용을 계산하는 프로그램을 작성하시오.
입력
첫째 줄에는 정점 및 색깔의 개수 n(1≤n≤100,000)이 주어진다. 다음 n-1개의 줄에는 각 줄에 두 개의 정수로 주어진 트리 상에서 연결되어 있는 두 정점의 번호가 주어진다.
출력
첫째 줄에 최소 비용을 출력한다.
📂아이디어 및 알고리즘(문제 풀이)
- 문제 접근
- 제일 먼저 생각할건 n개의 노드를 칠하는데 과연 몇 종류의 색이면 전부 칠할 수 있을까??
- 결론은 n개의 노드를 칠하는데 대략 log2 n 종류의 색만 필요하게 된다. (밑에 증명)
- S(n) : 각 색마다 색칠하는 비용이 다를 때, n개의 색칠로 표현할 수 있는 최대 노드 개수!
- S(n)에는 root노드가 n(n의 색)이고 서브 트리로 1~(n-1)[1~n-1의 색] 구성되어 있다.
- S(n) >= 1(root 노드) + S(1) + S(2) + ... + S(n-1) 이라는 수식을 얻을 수 있다.
- S(1) = 1, S(2) = 2의 값을 통해 수학적 귀납법으로 풀게 되면
- S(n) >= 2^(n-1) 값을 얻을 수 있게 된다.
- 결국은 S(18) >= 2^17 이고 이 값은 10만보다 크기 때문에 18가지 색으로 전부 색칠이 가능한걸 의미한다.
- 이제 색칠할 때의 최솟값을 알았기 때문에 트리를 구성하면서 DP로 해결하면 된다.
- 문제 풀이
- Arraylist<Arraylist<Integer>> list로 트리를 구성해주고 입력값을 바탕으로 간선 연결 정보를 등록해준다.
- TreeTable에서 부모 노드값과 현재 노드값, 현재 노드 색값을 인자로 넣어준다.
- 부모 노드값을 알기 때문에 list.get(현재 노드)를 순회할 때 위로 올라가지 못하도록 하였다.
- 자식 노드를 next index로 할때 min에 최댓값을 넣고 부모 컬러를 제외한 색을 자식 노드의 서브트리로 탐색해준다.
- 최초에 트리의 말단노드까지 이동하여 최소 컬러값을 넣고 Math.min(서브 트리, min)값을 비교하여 작은 값을 현재 노드 컬러값에 누적합을 시켜준다.
- 마지막으로 자신의 컬러값을 더해주어 return 시켜주게 되면 최솟값을 구할 수 있다.
📂소스 코드
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.PriorityQueue;
import java.util.Scanner;
public class Main {
static int MAX_color = 20;
static int DP[][] = new int[100004][MAX_color+1];;
static boolean choice[];
static int Min = Integer.MIN_VALUE;
// 4색 정리 : 모든 정점들을 겹치지 않게 4가지 색으로 전부 칠할 수 있다. (참고)
//DP[root][0~17]를 채우는 함수. 만약 자식 중에 table_set함수를 수행하지 않은 것이 있다면 자식 먼저 table_set하도록 한다.
public static int Tree_table_set(ArrayList<ArrayList<Integer>> list,int par,int index,int color) {
//D[i][color] : 나의 부모 색깔이 color일때 i 번째 node를 root로 하는 subtree 최솟값.
if(DP[index][color]!=0) return DP[index][color]; //값을 취합하는 과정에서 거꾸로 가는것을 방지
for(int next : list.get(index)) {
if(next == par) continue; //부모로는 못가게 막는다.
int min = Integer.MAX_VALUE; //최솟값 정의 => 값은 최대
for(int i=1;i<=MAX_color;i++) {
if(i == color) continue; //일단 부모 컬러는 제외 시킨다.
int t = Tree_table_set(list,index,next,i); //서브트리 탐색
//트리의 말단 노드까지 이동하고 최소 컬러 노드를 넣어준다.
min = Math.min(min, t);
}
DP[index][color] += min;
}
//말단 노드에 값을 넣어준다.
DP[index][color] += color;
return DP[index][color]; //그 값을 리턴해준다.
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
for(int i=0;i<=n;i++) {
list.add(new ArrayList<>());
}
for(int i=0;i<n-1;i++) {
int x = sc.nextInt();
int y = sc.nextInt();
list.get(x).add(y);
list.get(y).add(x);
}
list.get(0).add(1);
int result = Tree_table_set(list,-1,0,0);
System.out.println(result);
}
}
🔎출처
https://www.acmicpc.net/problem/1693
1693번: 트리 색칠하기
n개의 정점으로 이루어진 트리가 있다. 이 트리의 각 정점을 색칠하려고 한다. 색칠을 할 때에는 1, 2, 3, …, n번 색깔 중에 하나로 색칠하여야 한다. 각 색깔을 사용하여 한 개의 정점을 색칠할 때
www.acmicpc.net
'알고리즘(Java) > 그래프(Graph)' 카테고리의 다른 글
[JAVA]트리의 독립집합(BOJ-2213[그래프,DP]) (0) | 2020.11.28 |
---|---|
[JAVA]합리적인 이동경로(BOJ-2176[그래프,DP,다익스트라]) (0) | 2020.11.28 |
[JAVA]Strahler 순서(BOJ-9470 [그래프]) (0) | 2020.11.26 |
[JAVA]우수 마을(BOJ-1949 [그래프]) (0) | 2020.11.23 |
[JAVA]사회망 서비스(SNS)(BOJ-2553 [그래프]) (0) | 2020.11.22 |