POJ_3177
说实话,我现在还是不明白这个题目究竟是想让我们如何去处理问题。首先的疑惑就是如果连续输入两个1 2,1和2之间算一条路还是两条路,其次如果分别输入1 2及2 1,这时1和2之间算一条路还是两条路?
由于对上面数据的理解的不同,会用3种不同的处理方式:
①不管1 2还是2 1,就当做是1和2之间连通,也就是完全忽略题目中所说的两点之间可以有多条路,多条时只当有一条。
②两个1 2看做一条路,1 2及2 1看做两条路。
③两个1 2看做两条路,1 2及2 1也看做两条路。
其中根据①和②写出的程序都是可以AC的,而根据③写出的程序,依据我看到的一些讨论,应该是WA掉的。也正是因为这个,导致我对题意严重不解。
如果按思路①很好写程序的,邻接矩阵实现起来可能更方便一点,因为输入的时候就不用判断是否有重边了,空间上也是允许的。
这里再说一下我如果按思路②理解,我的一些想法。
首先如果按②理解的话,邻接矩阵实现起来可能不大好实现,如果用邻接表的话,我们可以先当做有向边来读,将重边删去,最后再把每个边反向存一遍。然后其余的代码全部按思路①的模式去写。
当然如果这么写代码,实际上就是按两点之间最多只有一条边来处理的,最后是求不出到底哪些是一起的边双连通分量的,但对结果没有什么影响。对于为什么没影响,我们可以枚举重边出现的位置来看一下。
首先,如果重边出现在原本的边双连通分中,那么肯定没有影响。再者,如果重边出现在两个不是“叶子”的边连通分量之间的桥的位置,那么这时也不会有影响,因为我们最终统计的是“叶子”的个数,中间的部分是没有影响的。再者,如果重边出现在是“叶子”的边双连通分量与其他边连通分量之间,那么这时在没有重边时的“叶子”在重边出现后就不是“叶子”了,同时,由于多了一个重边,在计算原来的“叶子”的出入度时就会多算上这个重边的,于是出入度的值便不会再体现出作为“叶子”的特征,于是便不会再把这个边双连通分量再看做“叶子”而增加“叶子”的计数了,也便对最后“叶子”的计数是没有影响的。
通过上面的分析表明,尽管我们最后得不到正确的边双连通分量,但对解题的结果是没有影响的,就好比强连通分量的题目中,我们以强连通分量中各个顶点的出度、入度的和作为这个强连通分量的出度、入度,尽管这样算出的出度、入度是不一定正确的,但对于我们统计出度为0和入度为0的强连通分量的个数是没有影响的。
下面的是按思路②写成的代码。
#include<stdio.h>
#include<string.h>
int first[5010],next[10010],v[10010],n,dgr[5010];
int time,dfn[5010],low[5010],paint[5010],num,s[5010],top;
void tarjan(int u,int fa)
{
int e;
dfn[u]=low[u]=++time;
s[top++]=u;
for(e=first[u];e!=-1;e=next[e])
if(v[e]!=fa)
{
if(!dfn[v[e]])
{
tarjan(v[e],u);
if(low[v[e]]<low[u])
low[u]=low[v[e]];
else if(low[v[e]]>dfn[u])
{
for(s[top]=-1;s[top]!=v[e];)
paint[s[--top]]=num;
num++;
}
}
else if(dfn[v[e]]<low[u])
low[u]=dfn[v[e]];
}
}
int main()
{
int i,j,k,r,a,b,u,e,leaves,temp,ok;
while(scanf("%d%d",&n,&r)==2)
{
memset(first,-1,sizeof(first));
e=0;
for(i=0;i<r;i++)
{
scanf("%d%d",&a,&b);
a--;
b--;
ok=1;
for(temp=first[a];temp!=-1;temp=next[temp])
if(v[temp]==b)
{
ok=0;
break;
}
if(ok)
{
v[e]=b;
next[e]=first[a];
first[a]=e;
e++;
}
}
k=e;
for(u=0;u<n;u++)
for(temp=first[u];temp!=-1;temp=next[temp])
if(temp<k)
{
v[e]=u;
next[e]=first[v[temp]];
first[v[temp]]=e;
e++;
}
memset(dfn,0,sizeof(dfn));
time=top=0;
memset(paint,0,sizeof(paint));
num=1;
for(i=0;i<n;i++)
if(!dfn[i])
tarjan(i,-1);
memset(dgr,0,sizeof(dgr));
for(u=0;u<n;u++)
{
for(e=first[u];e!=-1;e=next[e])
if(paint[u]!=paint[v[e]])
{
dgr[paint[u]]++;
dgr[paint[v[e]]]++;
}
}
leaves=0;
for(i=0;i<num;i++)
if(dgr[i]==2)
leaves++;
printf("%d\n",(leaves+1)/2);
}
return 0;
}