본문 바로가기

알고리즘/SW EXPERT

4534. 트리 흑백 색칠

4534. 트리 흑백 색칠


모든 경우를 탐색하는 것은 시간이 너무 오래 걸린다, 하지만 생각해보면 트리 순회처럼 재귀적으로 문제를 해결 할 수 있어 보였다.


그리고 최적 부분 구조라는 판단이 섰기 때문에, DP를 이용하여 문제를 풀어나갔다.


점화식은 'D[node][color] 로 node 에서 color 를 가질 때, 가질 수 있는 최대의 색칠 방법의 수' 라고 정의하였다.


이러한 점화식에 입각해서 생각해본다면, 


D[node][흰색] = D[next][검은색] + D[next][흰색]


D[node][검은색]=D[next][흰색]


이 된다.



###문제를 겪으면서 빠졌던 문제점###


처음에 계속 문제에서 빠뜨렸던 부분은, 한 노드에서 자식 노드가 2개 이상일 때에는, 자식 노드들의 최대 색칠 방법의 수를 전부다 곱해야 그 

노드의 최대 방법의 수가 나온다는 점이었다. 그래서 이 부분은 tmp 와 sum 함수를 for문에서 이용해서 해결할 수 있었다.


그리고 또 겪었던 문제점은 처음에 부모노드를 제외하고 연결된 노드가 있는지를 확인하는 과정에서 맨 처음에 visited[MAX] 배열을 선언해


서 방문체크 여부를 확인했었는데, 그럴경우에는 어떤 노드를 흰색에서 방문할 경우, 검정색에서 방문하지 못하는 문제점이 생겼다. 그래서 


이 부분은 그냥 재귀함수에서 parent 를 추가해줘서 메모리도 절약하면서 해결할 수 있었다.  


그리고 이런 문제에서 자주 실수하는 점은 변수의 범위이다. MOD 로 나누기 때문에 int 형 변수의 범위에서 해결할 수 있다는 착각을 하지만, 

계산과정에서 int 형 변수를 넘어가면 값의 오버플로우로 이상한 값이 저장되거나 그렇기 때문에 계산에 사용되는 변수는 모두 long long 으


로 바꾸어서 문제를 해결해야 했다. 또한 실수를 줄이기 위해 모든 계산에서 %MOD 연산을 적용시켰다.


<정답 코드>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include<iostream>
#include<vector>
#define MAX 100001
#define MOD 1000000007
using namespace std;
 
int N;
long long ans;
long long D[100001][2];
int go(int parent,int node, int color,vector<vector<int>> &v)
{
    // 부모노드를 제외하고 연결된 노드가 있는지 확인, 즉 리프노드 여부를 판단
    int cnt = 0;
    for (int i = 0; i < v[node].size(); i++)
    {
        int next = v[node][i];
        if (next==parent)
            continue;
 
        cnt++;
    }
 
    // cnt==0 이면 리프노드
    if (cnt == 0)
        return 1;
 
    // 이미 값이 존재하는 경우 return 
    if (D[node][color] != -1)
        return D[node][color] % MOD;
 
 
    D[node][color] = 0;
 
    // 검정색일때
    if (color == 0)
    {
        long long tmp = 1;
        for (int i = 0; i < v[node].size(); i++)
        {
            long long sum = 0;
            int next = v[node][i];
            // 연결된 노드가 부모노드가 아닐 때
            if (next!=parent)
            {
                // 다음 노드에서의 최대값의 합을 구함
                sum += (go(node,next, 1, v)) % MOD;
                sum %= MOD;
 
                // node에 연결된 여러개의 next 의 합의 곱을 이용하여 총 갯수를 구함.
                tmp *= sum;
                tmp %= MOD;
            }
        }
        // 구한 값을 저장
        D[node][color] = tmp;
    }
    
 
    // 흰색일때
    if(color==1)
    {
        long long tmp = 1;
        for (int i = 0; i < v[node].size(); i++)
        {
            long long sum = 0;
            int next = v[node][i];
 
            // 연결된 노드가 부모노드가 아닐 때
            if (next!=parent)
            {
                // 다음 노드에서의 최대값의 합을 구함
                sum += ((go(node,next, 0, v)) % MOD) + ((go(node,next, 1, v)) % MOD);
                sum %= MOD;
 
                // node에 연결된 여러개의 next 의 합의 곱을 이용하여 총 갯수를 구함.
                tmp *= sum;
                tmp %= MOD;
            }
 
        }
 
        // 구한 값을 저장
        D[node][color] = tmp;
    }
 
    return D[node][color];
 
 
}
int main()
{
    //freopen("input.txt", "r", stdin);
    int tc;
    cin >> tc;
 
    for (int t = 1; t <= tc; t++)
    {
        for (int i = 0; i < MAX; i++) {
            D[i][0= -1;
            D[i][1= -1;
        }
 
        cin >> N;
        vector<vector<int>> v(N+1);
 
        // 간선 연결
        for (int i = 1; i < N; i++)
        {
            int from, to;
            cin >> from >> to;
            v[from].push_back(to);
            v[to].push_back(from);
        }
 
        ans = (go(-1,10, v)%MOD + go(-1,11, v)%MOD)%MOD;
 
        cout <<"#"<<t<<" "<< ans << endl;
 
    }
 
    return 0;
}
cs


반응형

'알고리즘 > SW EXPERT' 카테고리의 다른 글

3184번  (0) 2018.10.08
4583. 세븐 카드 섞기 게임  (0) 2018.10.06
5432. 쇠막대기 자르기  (0) 2018.10.06
5648. 원자 소멸 시뮬레이션  (0) 2018.10.04
5653. 줄기세포배양  (2) 2018.10.02