割点和图(割边)

割点: 在连通的无向图中,删除一个点和与它所相连的边,无向图不再连通,说明这个点是割点。
思路: 从一个点开始深搜,当这个点不经过他的父亲节点不能返回到比这个父节点更早的时间戳,说明这个点的父节点是割点。
low[v]>=dfn[u]
注意: 判断割点时分两种情况,如果父节点是根节点的话,至少两个儿子才能形成割点!
割边就是去掉一条边使这个图不连通。
low[v]>dfn[u];不能经过u->v这条边,所以不能等于low[u] (可以用三角形举例)

/*POJ - 1144 求割点数目*/
#include<stdio.h>
#include<vector>
#include<string.h>
using namespace std;
vector<int>edge[110];
int cnt;
int dfn[110],low[110],root,flag[111];
/*考虑图不连通?*/
void Tarjan(int u,int fa)
{
    int child=0;
    dfn[u]=low[u]=++cnt;
    for(int i=0; i<edge[u].size(); i++)
    {
        child++;/*!!! 统计儿子数量*/
        int v=edge[u][i];
        if(!dfn[v])/*深搜*/
        {
            Tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]&&u!=root)/*!!!大于等于*/
            {
                flag[u]=1;/*儿子回到的最早时间戳大于等于x,x不是根节点*/
            }
            else if(u==root&&child>=2)
            {
                /*根节点,至少两个儿子*/
                flag[u]=1;
            }
        }
        else if(u!=v)/*!!! v点走过*/
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    return ;
}
int main()
{
    int n;
    while(~scanf("%d",&n)&&n)
    {
        int m,t;
        char c;
        root=1;
        cnt=0;
        for(int i=0; i<=n; i++)
        {
            edge[i].clear();
            dfn[i]=0;
            low[i]=0;
            flag[i]=0;
        }
        while(~scanf("%d",&m)&&m)
        {
            while(1)
            {
                scanf("%c",&c);
                if(c=='\n')
                    break;
                scanf("%d",&t);
                edge[m].push_back(t);
                edge[t].push_back(m);
               // printf("%d <---> %d\n",t,m);
            }
        }
        Tarjan(1,1);
        int sum=0;
        for(int i=1; i<=n; i++)
            if(flag[i])
                sum++;
        printf("%d\n",sum);
    }
    return 0;
}

/*割边*/
#include<bits/stdc++.h>
using namespace std;
#define N 1000
int n,m,first[N],nex[N],w=1,dfn[N],low[N],flag[N],u[N],v[N],root,index;
void Tarjan(int x,int father)
{
    int child=0;
    index++;/*计算时间戳*/
    dfn[x]=low[x]=index;
    for(int i=first[x];i!=-1;i=nex[i])
    {
        int y=v[i];/* x->y */
        if(!dfn[y])/*没有被访问过*/
        {
            Tarjan(y,x);
            low[x]=min(low[x],low[y]);
            if(low[y]>dfn[x])/*这条边是不是割边*/
                printf("%d %d\n",x,y);
        }
        else if(x!=father)/*不通过父节点访问*/
            low[x]=min(low[y],dfn[x]);/*在割点中不一样low的值不对*/
    }
    return;
}
int main()
{
    index=0;
    int y,x;
    scanf("%d%d",&n,&m);
    memset(flag,0,sizeof(flag));
    memset(first,-1,sizeof(first));
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&x,&y);/*无向图*/
        u[w]=x,v[w]=y;
        nex[w]=first[x];
        first[x]=w;
        w++;
        u[w]=y,v[w]=x;
        nex[w]=first[y];
        first[y]=w;
        w++;
    }
    root=1;/*根节点*/
    Tarjan(1,1);
    printf("割点为:\n");
    for(int i=0;i<=n;i++)
        if(flag[i])
        printf("%d\n",i);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值