割点和桥 poj1144

前几天参加省赛了,无语凝咽。。。
又学了一年竟然还不如大一的时候,还是踏实点吧


割点和桥 简单的说就是去掉一个点或者一条边 原来的图变成了两个或者更多的图
vis[v]记录的是节点v当前的访问状态,1表示在栈中,0,表示未访问,2表示已经访问过;
dfn[v]记录的是节点v被访问时的时间;
low[v]记录的是点v可以到达的访问时间最早的祖先
在深搜的过程中,对于当前节点cur,记录dnf[cur],和与它相连的节点i,两种情况
1.i未被访问过,深搜i,回溯的时候更新low[cur]=min(low[cur],low[i])
2.i在栈中,那么更新 low[cur]=min(low[cur],dnf[i])


如果cur是根节点并且孩子超过一个,那么cur是割点
如果cur不是根节点并且low[i]>=dfn[cur],即cur的所有孩子最早的祖先都没有超过当前节点,那么cur是割点


如果low[i]>dfn[cur],那么(cur,i)是桥
举个例子,深搜时


d=1 l=1  d=2 l=2
   1--------2--------5--------6
    |           |            |
    |           |            |
   3--------4--------|
d=4 l=4  d=3 l=3
当深搜到节点3的时候,存在边(1,3),因为节点1在栈中,那么low[3]=1,回溯过程更新节点4,变成
d=1 l=1  d=2 l=2
   1--------2--------5--------6
    |            |           |
    |            |           |
   3--------4--------|
d=4 l=1  d=3 l=1
继续深搜5,6,回溯更新节点2
d=1 l=1  d=2 l=1  d=5 l=5  d=6 l=6
   1--------2--------5--------6
    |           |            |
    |           |            |
   3--------4--------|
d=4 l=1  d=3 l=1

因为节点1只有一个孩子(节点2,没有节点4),而对于节点5,low[6]>dfn[5],所以节点5为割点


# include <stdio.h>
# include <string.h>
# include <iostream>
using namespace std;
const int maxn=105;
int map[maxn][maxn];
int dfn[maxn],low[maxn],vis[maxn];
int res[maxn];
void cut(int cur,int father,int dep,int n)
{
    vis[cur]=1;
    dfn[cur]=low[cur]=dep;
    int i;
    int children=0;
    for(i=1; i<=n; i++){
        if(map[cur][i]){
            if(vis[i]==1&&i!=father)  low[cur]=min(low[cur],dfn[i]);
            if(vis[i]==0)
            {
                cut(i,cur,dep+1,n);
                children++;
                low[cur]=min(low[i],low[cur]);
                if(father==-1&&children>1 || father!=-1&&low[i]>=dfn[cur]) res[cur]=1;
            }

        }
    }
    vis[cur]=2;
}


int main()
{
    int n,ans;
    while(scanf("%d",&n),n)
    {
        memset(map,0,sizeof(map));
        memset(vis,0,sizeof(vis));
        memset(res,0,sizeof(res));
        //memset(dfn,0,sizeof(dfn));
        //memset(low,0,sizeof(low));
        int a,b;
        ans=0;
        while(scanf("%d",&a),a){
            while(scanf("%d",&b)!=EOF)
            {
                map[a][b]=1;
                map[b][a]=1;
                //printf("%d\n",b);
                char c;
                c=getchar();
                if(c=='\n') break;
            }
        }
        cut(1,-1,0,n);
        for(int i=1; i<=n; i++) if(res[i]) ans++;
        printf("%d\n",ans);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值