Tarjan求缩点化强连通图

Describe:

  求一个有向图加多少条边可以变成一个强连通图

Solution:

  Tarjan缩点染色后,判断出度和入度,所有点的出度 = 0 的和 和 入度 = 0 的和的最大值即为所求。

缩点染色

for(int i = 1;i <= n;++i)
        {
            if(!dfn[i])
            {
                tarjan(i);
            }
        }
void tarjan(int s)
{
    dfn[s] = low[s] = ++tot;
    stk[stk_siz++] = s;
    instk[s] = true;

    for(int i = id[s];~i;i = e[i].pre)
    {
        int to = e[i].to;
        if(!dfn[to])
        {
            tarjan(to);
            low[s] = min(low[s],low[to]);
        }
        else if(instk[to])
            low[s] = min(low[s],dfn[to]);
    }
    if(dfn[s] == low[s])
    {
        ++colid;
        while(stk_siz > 0 && stk[stk_siz] != s)
        {
            --stk_siz;
            int tmp = stk[stk_siz];
            instk[tmp] = false;
            col[tmp] = colid;
        }
    }
}

 进行每一个缩点后的出度入度判断

for(int i = 0;i < m;++i)
        {
            from = e[i].from;
            to = e[i].to;
            //咋忘了缩点了!!这是缩点后的操作
//            cout<<from<<" "<<to<<endl;
//            cout<<col[from]<<" "<<col[to]<<endl;
            if(col[from] != col[to])
            {
                in[col[to]]++;
                out[col[from]]++;
            }
        }
        int ret = 0,innum = 0,outnum = 0;
        for(int i = 1;i <= colid;++i)
        {
            if(!in[i])innum++;
            if(!out[i])outnum++;
        }
        ret = max(innum,outnum);

 还要注意的就是缩成一个点的时候,也就是本来就是一个强连通分量是不需要添加边的

Coding:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 2e4 + 1e3;
const int maxm = 5e4 + 1e3;
struct node{
    int from,to,pre;
    node(){}
    node(int to,int pre):to(to),pre(pre){}
}e[maxm];
int colid;
int id[maxn],cnt;
int col[maxn];
int in[maxn],out[maxn];
int dfn[maxn],low[maxn];
int tot;
int stk[maxn],stk_siz;

bool instk[maxn];
void add(int from,int to)
{
    e[cnt].to = to;
    e[cnt].from = from;
    e[cnt].pre = id[from];
    id[from] = cnt++;
}
void init()
{
    memset(id,-1,sizeof(id));
    memset(instk,0,sizeof(instk));
    memset(dfn,0,sizeof(dfn));
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    cnt = tot = colid = stk_siz = 0;
}
void tarjan(int s)
{
    dfn[s] = low[s] = ++tot;
    stk[stk_siz++] = s;
    instk[s] = true;

    for(int i = id[s];~i;i = e[i].pre)
    {
        int to = e[i].to;
        if(!dfn[to])
        {
            tarjan(to);
            low[s] = min(low[s],low[to]);
        }
        else if(instk[to])
            low[s] = min(low[s],dfn[to]);
    }
    if(dfn[s] == low[s])
    {
        ++colid;
        while(stk_siz > 0 && stk[stk_siz] != s)
        {
            --stk_siz;
            int tmp = stk[stk_siz];
            instk[tmp] = false;
            col[tmp] = colid;
        }
    }
}
int main()
{
    int t,n,m;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d%d",&n,&m);
        int from,to;
        for(int i = 1;i <= m;++i)
        {
            scanf("%d%d",&from,&to);
            add(from,to);
        }
        for(int i = 1;i <= n;++i)
        {
            if(!dfn[i])
            {
                tarjan(i);
            }
        }
        //边的存储是从1开始!!
        for(int i = 0;i < m;++i)
        {
            from = e[i].from;
            to = e[i].to;
            //咋忘了缩点了!!这是缩点后的操作
//            cout<<from<<" "<<to<<endl;
//            cout<<col[from]<<" "<<col[to]<<endl;
            if(col[from] != col[to])
            {
                in[col[to]]++;
                out[col[from]]++;
            }
        }
        int ret = 0,innum = 0,outnum = 0;
        for(int i = 1;i <= colid;++i)
        {
            if(!in[i])innum++;
            if(!out[i])outnum++;
        }
        ret = max(innum,outnum);
        //特殊判断一下,一个点的时候(一种颜色的时候就是强连通了)
        if(colid == 1)
            printf("%d\n",0);
        else
            printf("%d\n",ret);
    }
    return 0;
}

 

  

posted @ 2018-09-09 22:42  Butterflier  阅读(114)  评论(0编辑  收藏  举报