poj1144

Poj 1144
描述
一个电话线公司(简称TLC)正在建立一个新的电话线缆网络。他们连接了若干个地点分别从1到N编号。没有两个地点有相同的号码。这些线是双向的并且能使两个地点保持通讯。每个地点的线都终结于电话交换机。每个地点都有一个电话交换机。从每个地点都能通过线缆到达其他任意的地点,然而它并不需要直接连接,它可以通过若干个交换机来到达目的地。有时候某个地点供电出问题时,交换机就会停止工作。TLC的工作人员意识到,除非这个地点是不可达的,否则这种情况就会发生,它还会导致一些其它的地点不能互相通讯。在这种情况下我们会称这个地点(错误发生的地方)为critical。现在工作人员想要写一个程序找到所有critical地点的数量。帮帮他们。

输入
输入文件包括若组测试数据。每一组是一个网络,每一组测试数据的第一行是地点的总数量N<100. 每个接下来最多N行包括一个数字表示一个地点和与它相连接的地点的数字。这些最多N行完全描述了整个网络,比如,网络中每个直接连接的两个地点被至少一行包括。一行内的所有数字都要用空格隔开。每组数据需要用单独的一个0结束。最后的块只有一行即N=0。

输出
输出除了最后一个组其他每一个组的critical地点的数量,每个块用一行输出。

样例输入:
5
5 1 2 3 4
0
6
2 1 3
5 4 6 2
0
0

样例输出
1
2

提示:
你需要确定每行的结束。为了方便判断,每行的结束都没有多余的空白

割点判定
对于DFS树根, 判断度数是否大于1
对于其他点u, 如果不是根的直接儿子, 且low[u] >= dfn[P[u]], 则它的父亲v=P[u]是割点

#include
#include
struct edge{int v,next;}e[20010];
int low[10010],dfn[10010],cut[10010],first[10010];
//cut 记录他是否是截点 
int index,n,a,son,b,i,vi,en;
//index 当作时间的计数器 ,n  点的个数,son 根节点的孩纸数量
void addedge(int a,int b){e[en].v=b;e[en].next=first[a];first[a]=en++;}
//建立领接表
void clean()
{
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    memset(e,0,sizeof(e));
    memset(cut,0,sizeof(cut));
    memset(first,0,sizeof(first));
    index=0;en=0;son=0;
        /*因为少清理了一组数据所以居然出现了一组有答案,一组没有,心好累
        memset(first,0,sizeof(first));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(cut,0,sizeof(cut));
        memset(e,0,sizeof(e));
        en=0;index=0;
        index=0;*/
}
void tarjan(int rt,int fa)
{
    int son=0;//每个点单独计算自己的儿子
    low[rt]=dfn[rt]=++index;
    for(i=first[rt];i;i=e[i].next)
    {
        vi=e[i].v;//相连的点
        if(!dfn[vi])//如果还没有被发现过就递归
        {
            tarjan(vi,rt);son++;//新发现一个儿子加起来
            low[rt]=low[rt]if((dfn[rt]<=low[vi]&&rt!=1)||(rt==1&&son>1))cut[rt]=1;
            //如果dfn【rt】《low【vi】说明vi后面不能回到rt的祖先,说明没有环,
            //如果把rt减掉 rt的祖先就和vi及其子树不联通,所以rt为截点
            //我是以1为根节点,如果rt==1,那他是根节点,如果同时son》1,根节点
            //且不止一个孩纸,应该就是截点了
        }
        if(vi!=fa) {low[rt]=low[rt]//返租边 这个是防止少解,重边?反正示例数据没有vi!=fa会少解
    }
}
int out()
{
    int t=0;//把点遍历一边,有标记为一的计数器加一
    for(i=1;i<=n;i++)
    {
        if(cut[i]==1)t++;
    }
    printf("%d\n",t);//输出的函数好简单,相比之读入
}
int main()
{

    while(scanf("%d",&n)&&n)
    {
          clean();
        while(scanf("%d",&a)&&a)
        {
            while(getchar()!='\n')//为什么这里不会把输入流里的东西拿走 ,还是确实拿走了?
            {
                scanf("%d",&b);
                addedge(a,b);
                addedge(b,a);
            }
        }//这东西确实抄的啦,完全不会啦
        tarjan(1,1);//以1为根节点,他自己当父亲
        out();
    }
    return 0;
}

你可能感兴趣的:(poj1144)