Boj 4013) Atm
문제
설명
일단 누가봐도 Tree 에서의 DP 문제이다. 하지만 Cycle Graph 에서는 DP 을 사용하기가 어렵다. 이미 방문한 Node 와 Cycle 간의 구분이 힘들기 때문이다. 그래서 SCC 를 사용해 DAG 로 만들 필요가 있다.
SCC 를 이용한 그래프 압축을 했다면 DP 를 수행할 수 있다. 이를 구현하는 법은 여러가지겠지만 DFS 를 이용한 아래의 방법은 두가지 포인트를 갖는다.
- 이제 Graph 의 각 Node 가 아니라 SCC 전체의 값을 사용해 값을 업데이트 해간다. DAG 의 특성 상 SCC 가 달라지는 지점에 한번만 SCC 의 값을 사용해도 충분하다.
- 계산된 값을 저장하는 단위는 Vertex 단위가 아니라 SCC 단위로 한다. 다시말해 아래 코드의
dp[]
에 사용되는 인덱스가 SCC 의 인덱스어야 한다는 것이다. 그렇지 않으면 같은 SCC 의 Node 라도 값이 다르게 되어 문제가 발생한다. 이 부분이 디버그해서 잡아내기가 힘드므로 주의하자.
여기에 추가로 시작지점과 종료후보지점을 처리해야한다.
- 시작지점은 문제를 간단하게 해준다. 시작지점에서 도달 불가능한 Node 는 탐색할 필요가 없기 때문이다. 일반적 SCC 가 모든 Node 에 대해서 한번씩 탐색을 해야한다면 여기서는 시작지점에서만 DFS 를 돌리면 된다.
- 종료후보지점은
DFS()
를 수행할 때 종료지점에 도달했는지를 기록해서 수행한다. 단순히dp[]
에 정수를 저장해 음수 등으로 종료지점 도달여부를 수행하면 일이 복잡해진다.bool
값을 따로 쓰자.
시간 복잡도
O(\(\vert \mathrm{V} \vert + \vert \mathrm{E} \vert\))
코드
const int MAX_IN = 500001;
vector<int> lines[MAX_IN];
stack<int> st;
int visits[MAX_IN], n_visit;
int roots[MAX_IN];
bool bRest[MAX_IN];
int cashes[MAX_IN];
vector<pair<int, bool>> scces;
int SCC(int cur)
{
int res = visits[cur] = ++n_visit;
st.push(cur);
for (auto l : lines[cur])
{
if (roots[l]) continue;
if (visits[l]) res = min(res, visits[l]);
else res = min(res, SCC(l));
}
if (res == visits[cur])
{
auto& scc = scces.emplace_back();
const int th = scces.size();
while (1)
{
int tmp = st.top();
roots[tmp] = th;
scc.first += cashes[tmp];
scc.second |= bRest[tmp];
st.pop();
if (tmp == cur) break;
}
}
return res;
}
pair<int, bool> dp[MAX_IN];
pair<int, bool> DFS(int cur, int parent = -1)
{
if (visits[cur] == 0) return dp[roots[cur]];
visits[cur] = 0;
auto& res = dp[roots[cur]]; // 초기 {0, false} 상태
for (auto l : lines[cur])
{
auto t = DFS(l, cur);
if (t.second)
{
res.second |= true;
res.first = max(res.first, t.first);
}
}
auto& scc = scces[roots[cur] - 1];
res.second |= scc.second;
if (res.second && (parent == -1 || roots[cur] != roots[parent])) // Component 전체값 업데이트
res.first += scc.first;
return res;
}
int main()
{
int n, m; cin >> n >> m;
for (int i = 0; i < m; i++)
{
int a, b; cin >> a >> b;
lines[a].push_back(b);
}
for (int i = 1; i <= n; i++) cin >> cashes[i];
int S, P; cin >> S >> P;
for (int i = 0; i < P; i++)
{
int t; cin >> t;
bRest[t] = true;
}
SCC(S);
cout << DFS(S).first;
}
댓글남기기