强连通分量(缩点+求出入为0的点的个数)HDU 2767 Proving Equivalences

博客详细介绍了如何运用Trajan算法解决强连通分量问题,通过分析HDU 2767题目实例,阐述了缩点过程及找出入度为0的点的方法,帮助读者深入理解该算法。

用了半天的时间将强连通分量中的trajan算法及其代码实现这部分终于理解通透了。下面是我针对一道例题对其进行的阐释。

题目大意:

  给你一个有向图,问你在图中最少要加多少条边能使得该图变成一个强连通图.
题目思路:

首先我们求出该图的各个强连通分量,然后把每个强连通分量看出一个点(即缩点),然后我们得到了一个有向无环图(DAG).

        对于一个DAG,我们需要添加max(a,b)条边才能使其强连通.其中a为DAG中出度为0的点总数,b为DAG中入度为0的点总数.

        注意特殊情况:如果图已经强连通了,我们需要添加的边是0条,而不是1条.
这道题是参照http://blog.youkuaiyun.com/u013480600/article/details/31805017
经过自己的理解,将代码进行的分析。

感受:

运用了缩点,学会了找出入度为0的点的求法

#include<iostream>
#include<stdio.h>
#include<vector>
#include<stack>
#include<string.h>
#include<algorithm>
const int maxn=20000+10;
using namespace std;
vector<int>G[maxn];
stack<int>S;
int dfs_clock;
int scc_cnt;//强连通分量的个数
int n,m;
int pre[maxn];
int low[maxn];
int sccno[maxn];
bool in0[maxn],out0[maxn];
void dfs(int u)
{
    pre[u]=low[u]=++dfs_clock;
    S.push(u);//每搜一个就将其存入stack
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(!pre[v])//当pre为0的时候,就是还没有初始化,可见还没有搜到这个点。代替了visit
        {
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!sccno[v])//说明v还在G图里.值为0说明还未被踢
        {
            low[u]=min(low[u],low[v]);//这里有的人也写成low[u]=min(low[u],pre[v]),实际上结果都一样,只不过pre的值始终不变。而low的值一直在更新
        }
    }
    if(low[u]==pre[u])//说明u点是强分量起点
    {
        scc_cnt++;
        while(true)
        {
            int x=S.top();
            S.pop();
            sccno[x]=scc_cnt;//在这个地方进行了缩点操作,sccno数组记录的就是缩点后的值
            if(x==u)
                break;
        }
    }
}

void find_scc(int n)//此处将预处理和找scc强连通图进行了结合
{
    scc_cnt=dfs_clock=0;
    memset(pre,0,sizeof(pre));
    memset(sccno,0,sizeof(sccno));
    for(int i=0;i<n;i++)
    {
        if(!pre[i])
            dfs(i);
    }
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            G[i].clear();
        }
        while(m--)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            u--,v--;
            G[u].push_back(v);
        }
        find_scc(n);
        for(int i=1;i<=scc_cnt;i++)
        {
            in0[i]=out0[i]=true;
        }
        for(int u=0;u<n;u++)
        {
            for(int i=0;i<G[u].size();i++)
            {
                int v=G[u][i];
                if(sccno[u]!=sccno[v])
                {
                    out0[sccno[u]]=in0[sccno[v]]=false;//由于u->v,u的入度为0,出度不为0;而v反之。所以这个出入对应要消掉。in0[]为true表示入度为0,out[]为true表示出度为0
                }
            }
        }
        int a=0;int b=0;
        for(int i=1;i<=scc_cnt;i++)
        {
            if(in0[i])
                a++;
           if(out0[i])
                b++;
        }
        int ans=max(a,b);
        if(scc_cnt==1)//这一步一定要有。因为此图是一个强连通,则添加的为0
            ans=0;
        printf("%d\n",ans);
    }
    return 0;

}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值