SWExpertAcademy의 트리 흑백색칠(4534 / D5) 문제이다.
[ 문제풀이 ]
본인은 이 문제를 완전탐색, 그 중에서도 깊이 우선 탐색(DFS) 기법을 이용해서 접근하였다.
그런데 ! 단순히 완전탐색을 이용해서 문제를 풀게 된다면 주어지는 노드의 갯수가 최대 10만개 이기 때문에
시간초과가 발생하게 된다.
따라서 DFS + 메모이제이션을 이용해서 접근하였다.
가장 먼저, 가장 첫 노드는 어떤 노드로 설정해 주어도 상관이 없다.
본인은 무난하게 1번노드를 첫 번째 노드로 설정하고 탐색을 시작해주었다.
첫 번째 노드는 하얀색이든 검은색이든 뭘로 색칠을 해도 상관이 없다.
따라서, 하얀색으로 칠하면서 탐색을 시작하는 경우와, 검은색으로 칠하면서 탐색을 칠하는 경우 2가지 경우를 모두 다
탐색한 후에, 각각의 결과값을 더해주어야 한다.
( 하얀색으로 시작했을 때 발생할 수 있는 경우의 수 + 검은색으로 시작했을 때 발생할 수 있는 경우의 수 = 모든 경우의 수 )
본격적인 탐색을 할 때에는, 현재 노드가 하얀색이라면, 그 다음 노드를 하얀색으로 칠하는 경우와 검은색으로 칠하는 경우를 각각 계산한 후에 더해주었다.
현재 노드가 검은색이라면, 반드시 그 다음 노드를 하얀색으로 칠하는 경우만 탐색을 해 주었다.
정리해서 이야기해보면, 현재 노드가 x번 노드이고, 그 다음 칠해야 할 노드 nx번 노드라고 한다면,
x번 노드를 하얀색으로 칠했을 때, 트리를 칠할 수 있는 경우의 수 =
nx번 노드를 하얀색으로 칠했을 때 발생할 수 있는 경우의 수 + nx번 노드를 검은색으로 칠했을 때 발생할 수 있는 경우의 수
x번 노드를 검은색으로 칠했을 때, 트리를 칠할 수 있는 경우의 수 = nx번 노드를 하얀색으로 칠했을 때 트리를 칠할 수 있는 겨우의 수 가 된다는 것이다.
이를 코드로 표현하기 위해서 DP[][]라는 2차원 배열을 하나 사용해 주었는데,
DP[A][B] = C 의 의미는, "A번 노드를 B색깔로 칠했을 때, 전체 트리를 칠할 수 있는 경우의 수는 총 C개입니다." 를 의미한다.
따라서, 배열에서 'B'가 들어가는 부분의 크기는 2칸만 설정해 주었다. 왜냐하면, 칠할 수 있는 색이 하얀색, 검은색 2가지 밖에 없기 때문이다.
또한, 메모이제이션을 이용하기 위해서, "아직 탐색하지 않은 상황을 -1로 미리 설정" 해주었다.
만약, -1이 아닌 0으로 그대로 둔다면, DP[A][B] = 0 이라는 결과값이 있다면, A번 노드를 B색깔로 칠했을 떄, 전체 트리를 칠할 수 있는 경우의 수가 없다라는 것을 의미하는지, 아니면 계산된적이 없다는 건지 구분할 수 없기 때문이다.
따라서, -1로 초기값을 세팅해 주었고, DP[A][B]의 값이 -1이라면, 탐색을 한 적이 없었으므로 탐색을 진행해주면 되고,
만약 -1이 아니라면, 이미 탐색해본 적이 있는 상황이니, 그대로 DP[A][B]의 값을 return 하는 방식으로 진행해 주었다.
[ 소스코드 ]
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 | #include <iostream> #include <cstring> #include <vector> #define endl "\n" #define MAX 100010 #define MODULER 1000000007 using namespace std; int N; long long Answer; long long DP[MAX][2]; vector<int> Node[MAX]; void Initialize() { for (int i = 0; i < MAX; i++) Node[i].clear(); memset(DP, -1, sizeof(DP)); } void Input() { cin >> N; for (int i = 0; i < N - 1; i++) { int a, b; cin >> a >> b; Node[a].push_back(b); Node[b].push_back(a); } } long long DFS(int Cur, int Color, int Parent) { if (DP[Cur][Color] != -1) return DP[Cur][Color]; DP[Cur][Color] = 1; for (int i = 0; i < Node[Cur].size(); i++) { int Next = Node[Cur][i]; if (Next == Parent) continue; if (Color == 0) { DP[Cur][Color] = DP[Cur][Color] * (DFS(Next, 0, Cur) + DFS(Next, 1, Cur)); DP[Cur][Color] = DP[Cur][Color] % MODULER; } else { DP[Cur][Color] = DP[Cur][Color] * (DFS(Next, 0, Cur)); DP[Cur][Color] = DP[Cur][Color] % MODULER; } } return DP[Cur][Color]; } void Solution() { long long Result = DFS(1, 0, -1) % MODULER; long long Result2 = DFS(1, 1, -1) % MODULER; Answer = Result + Result2; Answer = Answer % MODULER; } void Solve() { int Tc; cin >> Tc; for (int T = 1; T <= Tc; T++) { Initialize(); Input(); Solution(); cout << "#" << T << " " << Answer << endl; } } int main(void) { ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); //freopen("Input.txt", "r", stdin); Solve(); return 0; } | cs |
'[ SWEA Code ] > # SWEA - ' 카테고리의 다른 글
[ SWEA 7730 ] 나무 깎는 홍준 (C++) (0) | 2020.08.27 |
---|---|
[ SWEA 9015 ] 배열의 분할 (C++) (0) | 2020.08.25 |
[ SWEA 7673 ] 영이는 영이 시져시져! (C++) (2) | 2020.08.19 |
[ SWEA 1798 ] 범준이의 제주도 여행 계획 (C++) (6) | 2020.08.15 |
[ SWEA 3421 ] 수제버거 장인 (C++) (0) | 2020.08.04 |