poj2762强连通分量+判断单链

题意:

有一个有向图,问你任意的v 和u都存在一个路径使得v->u或者u->v联通;

理解:

如果是 和 而不是 或 的话就是强连通分量的定义了,只要用trajan判断只存在一个强连通分量即可;;;;

但是本题是或,所以称为弱联通分量,对于弱联通分量需要满足只能是一条单链,有两种方法:

1.缩点后,拓扑排序,但是我没用

2.缩点后找入度为零的根节点,用dfs遍历,只要最长的那个支路是遍历的节点数是等于总的缩点后的节点就满足


注意:

1.开始时自己犯傻了,认为入度为零的点和出度为零的点都是1就可以,其实是错的,因为入度和出度都为1不可以说明是一条链,只能说名根节点和叶节点都是1,中间的节点是不是链就不可以保证,所以不可以这样;

2.初始化,每次都要是初始化整个tree,老是忘了,以后一定记得;;

3.多数情况下要考虑一下只有一个pid分量的情况,因为有时只有一个强连通分量可能不满足普遍的规律,所以要考虑一下,避免wa,当然这一题没问题;


解体步骤代码;

1.main函数

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(vis,0,sizeof(vis));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(bel,0,sizeof(bel));
        cnt = 0;pid = 0;
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            tree[i].clear();
            treeT[i].clear();
        }
        while(m --)
        {
            int s,v;
            scanf("%d%d",&s,&v);
            tree[s].push_back(v);
            treeT[v].push_back(s);
        }
        for(int i=1;i<=n;i++)
        {
            if(dfn[i] == 0)
            {
                vis[i] = true;
                stk.push(i);
                trajan(i);
            }
        }
        int ans1 = search(n);
        if(ans1 == 1)
            printf("Yes\n");
        else
            printf("No\n");
    }
}
2.trajan()判断强连通分量
int dfn[MAXN];
int low[MAXN];
int vis[MAXN];
int bel[MAXN];
int num[MAXN];
int cnt = 0,pid = 0;
void trajan(int v)
{
    dfn[v] = low[v] = ++cnt;
    for(int i=0;i<tree[v].size();i++)
    {
        int temp = tree[v][i];
        if(dfn[temp] == 0)
        {
            vis[temp] = true;
            stk.push(temp);
            trajan(temp);
            low[v] = min(low[v],low[temp]);
        }
        else if(vis[temp] == true)
        {
            low[v] = min(low[v],low[temp]);
        }
    }
    if(low[v] == dfn[v])
    {
        pid++;
        int t;
        do
        {
            t = stk.top();
            bel[t] = pid;
            vis[t] = false;
            stk.pop();
        }while(t != v);
    }
}
3.找根结点,对于入度为零可以使用转置图找出度的,对于出度为零节点的寻找只需要遍历就可以了,转置图为main中的treeT[MAXN];
int find(int n)
{
    memset(num,0,sizeof(num));
    int rec = 0;int c = 0;
    for(int i=1;i<=n;i++)
    {
        for(int ii = 0;ii<treeT[i].size();ii++)
        {
            int temp = treeT[i][ii];
            if(bel[temp]!=bel[i])
            {
                num[bel[i]]++;
            }
        }
    }
    for(int i=1;i<=pid;i++)
    {
        if(num[i] == 0) c++;
    }
    for(int i=1;i<=n;i++)
    {
        if(num[bel[i]] == 0){rec = i;}
    }
    if(c == 1) return rec;
    else return false;
}
4.搜索dfs()
int dfsmax = 0;
int flag = 0;
int mark[MAXN];
void dfs(int n,int rt,int count)
{
    if(flag == 1) return;
    for(int i=0;i<tree[rt].size();i++) if(!mark[tree[rt][i]])
    {
        int temp = tree[rt][i];
        mark[temp] = 1;
        if(bel[temp] == bel[rt])  dfs(n,temp,count);
        else dfs(n,temp,count+1);
    }
    dfsmax = max(count,dfsmax);
    if(dfsmax == pid - 1) flag = 1;
    return;
}
5.最后处理是不是ok,search()
int search(int n)
{
    memset(num,0,sizeof(num));
    int rt = find(n);
    if(rt == false) return false;
    dfsmax = 0;
    flag = 0;memset(mark,0,sizeof(mark));
    dfs(n,rt,0);
    if(dfsmax == pid - 1) return true;
    else return false;
}
然后就可以了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值