티스토리 뷰
1. 문제 링크
https://www.acmicpc.net/problem/2213
2213번: 트리의 독립집합
첫째 줄에 트리의 정점의 수 n이 주어진다. n은 10,000이하인 양의 정수이다. 1부터 n사이의 정수가 트리의 정점이라고 가정한다. 둘째 줄에는 n개의 정수 w1, w2, ..., wn이 주어지는데, wi는 정점 i의 가중치이다(1 ≤ i ≤ n). 셋째 줄부터 마지막 줄까지는 에지 리스트가 주어지는데, 한 줄에 하나의 에지를 나타낸다. 에지는 정점의 쌍으로 주어진다. 입력되는 정수들 사이에는 콤마가 없고 대신 빈칸이 하나 혹은 그 이상 있다. 가중치
www.acmicpc.net
2. 문제 개요
가중치가 가장 큰 최대 독립 집합을 찾는 것.
3. 문제 힌트
DP를 사용하자. 나를 포함하거나 포함하지 않거나를 기준으로.
경로 역추적을 하기 위해서 포함했을 때, 체크를 해줄 수 있도록 bool 배열을 하나 만든다.
4. 문제 풀이
주어진 가중치값과 경로를 인접 리스트에 넣어준다.
그리고 편의상 DFS탐색을 위한 리스트도 만들어 주었다. ( 1 -> 2 -> 1 -> 2... 와 같은 것 방지)
시작점(1)으로 부터 시작점을 포함한 경우, 포함하지 않은 경우 중 큰 값을 선택해야 한다.
dp(int cur, bool include) 함수를 정의하는데, cur 즉, 현재 점을 include 포함하느냐 안 하느냐 이다.
int v1 = dp(1, false)
int v2 = dp(1, true)를 비교해야 한다.
재귀적으로 들어가서 가장 마지막 노드까지 가야 한다. 그래서 값을 쌓아 올린다는(?) 느낌으로..
나를 포함하는 경우는 나와 인접한 노드는 무조건 포함하지 않아야 한다.
for (모든 인접한 노드에 대하여)
ans += dp(인접한 노드, false)와 같이 호출해야 한다.
나를 포함하지 않는 경우는 나와 인접한 노드는 포함해도 되고 포함하지 않아도 된다.
for(모든 인접한 노드에 대하여)
int v1 = dp(인접 노드, false)
int v2 = dp(인접 노드, true)
if(포함한 것이 포함하지 않은 것보다 크다면)
포함 여부 배열[인접 노드] = true
else
포함 여부 배열[인접 노드]=false
가 될 것이다.
그리고 역추적이 남았다.
역추적은 그냥 포함 여부 배열의 true인 부분을 차례대로 출력하면 될 것 같으나, 한참 포함되었다가 도중에 포함되지 않는 경우가 있을 수 있다. 따라서 시작했던 점을 기준으로 재귀적으로 들어가서 배열을 만들어야 한다.
tracert(int cur, bool include) 함수를 만들어서,
tracert(1, check [1])시작점을 기점으로 추적해야 한다.
cur점일 때 check [cur] == true라면, 포함한다는 뜻이고,
정답 배열에 삽입한다. 그리고 그것과 인접한 노드들은 모두 포함하지 않아야만 한다.
현재 노드가 포함되지 않을 경우에는 check배열에 저장했던 것을 따르도록 한다.
5. 코드
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
vector<vector<int>> adj;
vector<vector<int>> order;
vector<int>weight;
vector<int> answer;
int dptable[10001][2];
//안한것, 한것.
bool check[10001];
void tracert(int cur, bool include)
{
if (include) {
answer.push_back(cur);
for (int i = 0; i < order[cur].size(); i++) {
int next = order[cur][i];
tracert(next, 0);
}
}
else {
for (int i = 0; i < order[cur].size(); i++) {
int next = order[cur][i];
tracert(next, check[next]);
}
}
}
int dp(int cur, bool include)
{
int &cache = dptable[cur][include];
if (cache != -1) return cache;
if (include)
{
int ans = 0;
for (int i = 0; i < order[cur].size(); ++i){
int next = order[cur][i];
ans += dp(next, 0);
}
return cache = ans + weight[cur];
}
else
{
int ans = 0;
for (int i = 0; i < order[cur].size(); ++i) {
int next = order[cur][i];
int t1 = dp(next, 0);
int t2 = dp(next, 1);
if (t1 < t2)
check[next] = true;
else
check[next] = false;
ans += (max(t1, t2));
}
return cache = ans;
}
}
void dfs(int cur, int pre)
{
int len = adj[cur].size();
for(int i = 0; i < len; ++i)
{
int next = adj[cur][i];
if (next == pre)
continue;
order[cur].push_back(next);
dfs(next, cur);
}
return;
}
int main()
{
int n;
scanf("%d", &n);
adj.resize(n + 1);
weight.resize(n + 1);
order.resize(n + 1);
for (int i = 1; i <= n; ++i) {
scanf("%d", &weight[i]);
dptable[i][0] = dptable[i][1] = -1;
}
for (int i = 1; i < n; ++i) {
int from, to;
scanf("%d %d", &from, &to);
adj[from].push_back(to);
adj[to].push_back(from);
}
dfs(1, 0);
int t1 = dp(1, 1);
int t2 = dp(1, 0);
if (t1 > t2)
check[1] = true;
else
check[1] = false;
printf("%d\n", max(t1, t2));
tracert(1, check[1]);
sort(answer.begin(), answer.end());
for (int i = 0; i < answer.size(); ++i)
printf("%d ", answer[i]);
return 0;
}
지적, 댓글 언제나 환영입니다~
'알고리즘 > Dynamic Programming' 카테고리의 다른 글
백준, boj) 2579. 계단 오르기 ( C / C++ ) (2) | 2020.04.17 |
---|---|
boj, 백준) 1149. RGB거리 ( C / C++) (0) | 2020.04.17 |
백준, boj) 11726. 2xn 타일링(C / C++) (0) | 2020.04.16 |
Codeforces Round #627) F. Maximum White Subtree ( C / C++) (0) | 2020.04.11 |
BOJ) 1003. 피보나치 함수 ( C / C++) (0) | 2019.12.21 |