POJ 3177 Redundant Paths

POJ_3177

说实话,我现在还是不明白这个题目究竟是想让我们如何去处理问题。首先的疑惑就是如果连续输入两个1 212之间算一条路还是两条路,其次如果分别输入1 22 1,这时12之间算一条路还是两条路?

由于对上面数据的理解的不同,会用3种不同的处理方式:

①不管1 2还是2 1,就当做是12之间连通,也就是完全忽略题目中所说的两点之间可以有多条路,多条时只当有一条。

②两个1 2看做一条路,1 22 1看做两条路。

③两个1 2看做两条路,1 22 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;
}

  

  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值