Game Develop

[Algorithm]Baekjoon 20303번 :: 할로윈의 양아치 본문

Algorithm/Baekjoon

[Algorithm]Baekjoon 20303번 :: 할로윈의 양아치

MaxLevel 2024. 3. 5. 17:33

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

 

20303번: 할로윈의 양아치

첫째 줄에 정수 $N$, $M$, $K$가 주어진다. $N$은 거리에 있는 아이들의 수, $M$은 아이들의 친구 관계 수, $K$는 울음소리가 공명하기 위한 최소 아이의 수이다. ($1 \leq N \leq 30\ 000$, $0 \leq M \leq 100\ 000$,

www.acmicpc.net

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
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <math.h>
#include <queue>
#include <functional>
#include <sstream>
#include <memory.h>
#include <deque>
#include <set>
#include <unordered_map>
#include <stack>
#include <numeric>
 
using namespace std;
 
 
int n, m, k;
int candyCounts[30001= { 0 };
int groupCounts[30001= { 0 };
int parents[30001= { 0 };
int dp[30001][3001= { 0 };
vector<pair<intint>> groupes = { {0,0} };
 
int getParent(int node)
{
    if (node == parents[node]) return node;
    return parents[node] = getParent(parents[node]);
}
 
void unionParents(int nodeA, int nodeB)
{
    nodeA = getParent(nodeA);
    nodeB = getParent(nodeB);
 
    if (nodeA < nodeB)
    {
        parents[nodeB] = nodeA;
    }
    else
    {
        parents[nodeA] = nodeB;
    }
}
 
bool isSameParents(int nodeA, int nodeB)
{
    nodeA = getParent(nodeA);
    nodeB = getParent(nodeB);
 
    if (nodeA == nodeB) return true;
    return false;
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
 
    cin >> n >> m >> k;
 
    for (int i = 1; i <= n; ++i)
    {
        cin >> candyCounts[i];
        parents[i] = i;
        groupCounts[i] = 1;
    }
 
    for (int i = 0; i < m; ++i)
    {
        int a, b;
        cin >> a >> b;
 
        if (isSameParents(a, b)) continue;
 
        unionParents(a, b);
    }
 
    for (int i = 1; i <= n; ++i)
    {
        if (i != parents[i]) // 자기자신 아니면
        {
            int p = getParent(i);
            candyCounts[p] += candyCounts[i];
            groupCounts[p] += groupCounts[i];
        }
    }
 
    for (int i = 1; i <= n; ++i)
    {
        if (i != parents[i]) continue;
        
        groupes.push_back({ groupCounts[i], candyCounts[i] });
    }
 
    for (int i = 1; i < groupes.size(); ++i)
    {
        int groupCount = groupes[i].first;
        int groupCandy = groupes[i].second;
 
        for (int j = 1; j < k; ++j)
        {
            if (groupCount <= j) // 담을 수 있으면
            {
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - groupCount] + groupCandy);
            }
            else
            {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
 
    cout << dp[groupes.size()-1][k-1];
}
cs

 

Union-Find를 이용해서 각각의 서로소집합을 구한 후, k개 미만으로 최대의 캔디개수를 구하는 문제이다.

즉 각각의 집합들(원소개수, 원소들이 들고있는 캔디들의 합)으로, 배낭문제를 푼다고 생각하면 된다.

 

사실 푸는방법은 금방 알아냈고, 배낭풀이도 이제 바로바로 생각나서 코드작성이 가능한데 86번째줄 하나때문에 시간을 좀 먹었다. 해당 시점에서 여전히 parents[i]에 i의 최상위부모가 들어있다는게 보장되어지지 않기 때문에, getParent를 한번 더 호출해서 parents테이블을 업데이트해줘야 한다.