백준: Class 5 - 1005: 위상 정렬 알고리즘(Topological sort)

2 분 소요


</br> 1005번을 풀다 보니 시간 초과가 좀 나서, 찾아 보니 위상정렬이라는 게 있다고 한다. 그래서 한 번 공부해 보자.
</br>

위상정렬

    for(int i = 0; i < m; i++){
        int a, b;
        cin >> a >> b;
        v[a].push_back(b);
        ind[b]++;
    }
    queue<int> q;
    for(int i = 1; i <= n; i++){
        if( ind[i] == 0 ) q.push(i);
    }
    while( q.size() ){
        int now = q.front();
        q.pop();
        for(int i = 0; i < v[now].size(); i++){
            int nx = v[now][i];
            ind[nx]--;
            if( ind[nx] == 0 ) q.push(nx);
        }
    }

대충 이런 식으로, 그래프를 입력 받으며 i에 연결된 것들의 개수를 ind[i]에 저장한다.

즉 j -> i라면 ind[i]++

만약 ind[i]가 0이라면 큐에 넣는다.
큐를 탐색한다. now에서 나가는 엣지를 다 없앤다고 보면 된다. 그렇게 해서 ind[nx]가 줄게 되는데, 만약 0이 된다면 또 큐에 넣어 준다.

now -> nx라면 ind[nx]--

큐를 탐색하며 방문하는 순서가 위상정렬된 순서다.
</br>

1005: ACM Craft

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

방법 1.

int func(int now){
    if( dp[now] != -1 ) return dp[now];
    int ret = 0;
    for(int i = 0; i < graph[now].size(); i++){
        ret = max(ret, func(graph[now][i]));
    }
    dp[now] = nums[now] + ret;
    return nums[now] + ret;
}

// in main()
    while( t-- ){
        int n, k, w;
        cin >> n >> k;
        for(int i = 1; i <= n; i++){
            graph[i].clear();
            dp[i] = -1;
        }
        for(int i = 1; i <= n; i++){
            cin >> nums[i];
        }
        for(int i = 0; i < k; i++){
            int a, b;
            cin >> a >> b;
            graph[b].push_back(a);
        }
        cin >> w;

        cout << func(w) << '\n';
    }

물론 위상정렬을 안 쓰고 시간 초과 안 나게 푸는 방법도 있다.
dfs와 dp의 결합
말보다는 예제로 보여주면

ex)
1
7 8
10 1 100 10 3 1 1
1 2
1 3
2 4
3 4
4 5
4 6
5 7
6 7
7
    2(1)        5(3)
  /     \      /    \
1(10)     4(10)     7(1)
  \     /      \    /
    3(100)      6(1)    (진행 방향 ->)

dp[7] = 1 + max(dp[5], dp[6])
dp[6] = 1 + dp[4]
dp[5] = 3 + dp[4]
dp[4] = 10 + max(dp[2], dp[3])
dp[3] = 100 + dp[1]
dp[2] = 10 + dp[1]
dp[1] = 10

dp 없이 그냥 dfs 하면 시간 초과 나고,
dp 값 초기화를 0으로 하면 시간 초과 난다(경험)
어디부터 시작해야 할 지 모르므로 거꾸로 시작한다.
</br>

방법 2. 위상 정렬

#include <bits/stdc++.h>

using namespace std;

vector<int> graph[1001];
int nums[1001];
int ind[1001];
int times[1001];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int t;
    cin >> t;
    while( t-- ){
        int n, k, w;
        cin >> n >> k;
        for(int i = 1; i <= n; i++){
            graph[i].clear();
            ind[i] = 0;
            times[i] = 0;
        }
        for(int i = 1; i <= n; i++){
            cin >> nums[i];
        }
        for(int i = 0; i < k; i++){
            int a, b;
            cin >> a >> b;
            graph[a].push_back(b);
            ind[b]++;
        }
        cin >> w;
        queue<int> q;
        for(int i = 1; i <= n; i++){
            if( ind[i] == 0 ) q.push(i);
        }
        while( q.size() ){
            int now = q.front();
            q.pop();
            times[now] += nums[now];
            for(int i = 0; i < graph[now].size(); i++){
                int nx = graph[now][i];
                ind[nx]--;
                times[nx] = max(times[nx], times[now]);
                if( ind[nx] == 0 ) q.push(nx);
            }
        }
        cout << times[w] << '\n';
    }
}

위에서 본 위상정렬을 사용한다.
times[]에 시간을 저장하는데, times[nx]는 이전 것들을 다 기다려 줘야 하기 때문에, 자신과 이전 것 중 맥스 값을 저장해야 한다.
</br>


이거 아니까 쏠쏠하다
</br>

댓글남기기