본문 바로가기

ps(알고리즘)

Strongly Connected Componen

 

scc 문제를 해결하는 알고리즘은 kosaraju와 tarjan 두가지가 있다.

kosaraju 알고리즘은 두번의 dfs로 해결한다.

먼저 첫번째 dfs 순회를 하면서 각 정점들에 대해 방문완료시간을 계산한다.

방문완료시간을 기준으로 내림차순으로 역방향 그래프를 dfs로 순회한다.

이때 나눠지는 그룹들이 scc가 된다.

#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
int V,E;
vector<int> adj[100004],radj[100004];
int ft[100004];
int visit[100004],rvisit[100004];
int cnt;
int sccnum;
vector<int> sccList[100004];
int scc[100004];
int used[100004];
void dfs(int n)
{
    if(visit[n]==1) return;
    visit[n]=1;
    for(int i=0;i<adj[n].size();i++)
    {
        dfs(adj[n][i]);
    }
    ft[++cnt]=n;
}
void rdfs(int n)
{
    if(rvisit[n]==1) return;
    rvisit[n]=1;
    sccList[sccnum].push_back(n);
    scc[n]=sccnum;
    for(int i=0;i<radj[n].size();i++)
    {
        rdfs(radj[n][i]);
    }
}
int main()
{
    scanf("%d %d",&V,&E);
    for(int i=1;i<=E;i++)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        adj[a].push_back(b);
        radj[b].push_back(a);
    }
    cnt=0;
    for(int i=1;i<=V;i++)
    {
        if(!visit[i]) dfs(i);
    }
    sccnum=0;
    for(int i=cnt;i>=1;i--)
    {
        if(!rvisit[ft[i]])
        {
            sccnum++;
            rdfs(ft[i]);
        }
    }
    for(int i=1;i<=sccnum;i++)
    {
        sort(sccList[i].begin(),sccList[i].end());
    }
    printf("%d\n",sccnum);
    for(int i=1;i<=V;i++)
    {
        int x = scc[i];
        if(!used[x])
        {
            used[x]=1;
            for(int j=0;j<sccList[x].size();j++)
            {
                printf("%d ",sccList[x][j]);
            }
            printf("-1\n");
        }
    }
}

구현 코드이다.

 

두번째 알고리즘인 tarjan 알고리즘은 한번의 dfs로 scc를 구할 수 있다.

dfs를 돌면서 정점마다 id를 매기는데, 해당 정점에서 간선을 보면서, 방문하지 않은 곳은 방문하고, 그 정점을 스택에 넣는다.

그리고 그 정점의 parid를 dfs에서 반환한다. 그 반환값과 자신의 parid 중 작은 것을 parid로 저장한다.

간선에서 이미 방문한 곳인데 scc가 아닌 경우에는 그 정점의 id와 자신의 parid 중 작은 것을 parid로 저장한다. 

이렇게 모든 간선을 돌았을 때 parid와 자신의 id가 같다면 scc가 되고, 스택에 있는 점들을 자신이 나올 때까지 pop하면서 scc에 넣는다.

#include<stdio.h>
#include<stack>
#include<vector>
#include<algorithm>
using namespace std;
int V,E;
stack<int>nodes;
vector<int> adj[100004];
int scc[100004];
int id[100004];
int curid,sccnum;
vector<int> sccList[100004];
int used[100004];
int dfs(int n)
{
    curid++;
    id[n]=curid;
    int parid = id[n];
    nodes.push(n);
    for(int i=0;i<adj[n].size();i++)
    {
        int x = adj[n][i];
        //방문한적 없는 경우
        if(!id[x])
        {
            parid = min(parid, dfs(x));
        }
        //방문했는데 scc가 아닌 경우
        else if(!scc[x])
        {
            parid = min(parid, id[x]);
        }
    }
    if(parid==id[n])
    {
        sccnum++;
        while(!nodes.empty())
        {
            int t = nodes.top();
            nodes.pop();
            sccList[sccnum].push_back(t);
            scc[t]=sccnum;
            if(t==n) break;
        }
    }
    return parid;
}
int main()
{
    scanf("%d %d",&V,&E);
    for(int i=1;i<=E;i++)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        adj[a].push_back(b);
    }
    curid=0;
    for(int i=1;i<=V;i++)
    {
        if(!id[i]) dfs(i);
    }
    for(int i=1;i<=sccnum;i++)
    {
        sort(sccList[i].begin(),sccList[i].end());
    }
    printf("%d\n",sccnum);
    for(int i=1;i<=V;i++)
    {
        int x = scc[i];
        if(!used[x])
        {
            used[x]=1;
            for(int j=0;j<sccList[x].size();j++)
            {
                printf("%d ",sccList[x][j]);
            }
            printf("-1\n");
        }
    }
}

구현 코드이다.

 

'ps(알고리즘)' 카테고리의 다른 글

전깃줄-2  (0) 2024.12.03
볼록 껍질  (0) 2024.12.03
백조의 호수  (0) 2024.11.28
책 페이지  (1) 2024.11.26
오아시스 재결합  (0) 2024.11.26