n个城市之间有通讯网络,每个城市都有通讯交换机,直接或间接与其它城市连接。因电子设备容易损坏,需给通讯点配备备用交换机。但备用交换机数量有限,不能全部配备,只能给部分重要城市配置。于是规定:如果某个城市由于交换机损坏,不仅本城市通讯中断,还造成其它城市通讯中断,则配备备用交换机。请你根据城市线路情况,计算需配备备用交换机的城市个数,及需配备备用交换机城市的编号。
分析:无向图求割点。
求割点分两种情况:1,是根并且有超过一个孩子2,不是根,但是对于cur这个点来说,它的某一个孩子不管怎么样一定要经过cur才能到cur之前的点,说明cur是割点,具体操作是引入时间戳,d[i]记录第几个被访问到,low[i]记录在经过不是父亲的点时能到达的最小的d[i]。
注意概念:删除一条边仍然连通=边双连通=不含割边(所以如果flag数组都为0,说明是边双连通)
AC代码
#include <stdio.h>
#include <string.h>
int s[55][55];
int root;
int sum;
int flag[15];
int low[15];
int d[15];
int time;
int n;
int min(int a,int b)
{
return a<b?a:b;
}
void dfs(int cur,int father)
{
int child=0,i;
low[cur]=d[cur]=time++;
for (i=1;i<=n;i++)
{
if (s[i][cur]==1)
{
if (d[i]==0)
{
child++;
dfs(i,cur);
low[cur]=min(low[cur],low[i]);
if ((cur!=root)&&(low[i]>=d[cur]))//如果i的最低深度大于等于cur的到达时间,说明不管怎么从i到cur之前的总要经过cur,所以cur是割点
{
flag[cur]=1;
sum++;
}
if ((cur==root)&&(child==2))//如果cur是根,并且孩子数大于1
{
flag[cur]=1;
sum++;
}
}
else
if (i!=father)
low[cur]=min(low[cur],d[i]);
}
}
return;
}
int main()
{
int a,b,i,j;
freopen("gd.in","r",stdin);
freopen("gd.out","w",stdout);
scanf("%d",&n);
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
s[i][j]=0;
while(scanf("%d%d",&a,&b)==2)
{
s[a][b]=1;
s[b][a]=1;
}
root=1;
memset(flag,0,sizeof(flag));
memset(d,0,sizeof(d));
sum=0;
time=1;
dfs(1,root);
printf("%d\n",sum);
for (i=1;i<=n;i++)
if (flag[i]==1)
printf("%d\n",i);
return 0;
}