把这两个题写到一起,是因为这两题完全一样,用下面的代码两个题都可以AC。题目大意是给一个无向连通图,求添加最少边数使原图变成双连通图。由于是第一次写双连通图的缩点,所以WA了5次,在WA到AC的过程中,发现的错误如下:1、深度优先数没有初始化;2、深度优先数初始化错误;3、桥的判断有误;4、输入数据有重边,使用邻接表存储没有跳过重边。下面写一下这题的主要思路,用数组dfn保存各个结点的访问顺序(深度优先数),数组low[k]保存结点k及其子结点通过回边能到达的所有结点的最小深度优先数(下面简称最小深度优先数)。若子节点的最小深度优先数大于父结点的最小深度优先数,则连接父子结点的边为桥,否则该边不是桥。程序中使用了并查集来合并属于同一个双连通分量的所有结点(简称缩点),下面将简单证明这样做的正确性。要证明能够使用并查集来合并,只需证明点与点之间的双连通关系是等价关系即可。已知点a与点b双连通(a到b至少有两条不同路径),点b与点c双连通,求证点a与点c双连通。分两种情况讨论:1、点c在a到b的路径上,则结论显然成立;2、点c不在a到b的路径上,则路径ab与路径bc连接即可得到路径ac,同理可得路径ca。以上就证明了点与点双连通是等价关系。用并查集缩点后,得到的是一棵树,将一棵树通过加边的到双连通图需加最少边数为(叶子结点数目+1)/2。
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #define N 5001
5 #define MIN(a,b) ((a)<(b)?(a):(b))
6 #define SET(a,b) (memset(a,b,sizeof(a)))
7 struct node
8 {
9 int v;
10 struct node *next;
11 };
12 struct node *list[N];
13 int p[N],n,m;
14 int dfn[N],low[N],father[N],id[N],d[N],t;
15 char vis[N],flag[N][N];
16 void addEdge(int u,int v)
17 {
18 struct node *p=(struct node *)malloc(sizeof(struct node));
19 p->v=v,p->next=list[u],list[u]=p;
20 }
21 void make_set()
22 {
23 int i;
24 for(i=0;i<n;i++) p[i]=i;
25 }
26 int find_set(int i)
27 {
28 if(i!=p[i]) p[i]=find_set(p[i]);
29 return p[i];
30 }
31 void union_set(int i,int j)
32 {
33 i=find_set(i),j=find_set(j);
34 if(i==j) return;
35 p[j]=i;
36 }
37 void init()
38 {
39 make_set();
40 SET(vis,0),SET(father,-1);
41 dfn[0]=low[0]=vis[0]=t=1;
42 }
43 void dfs_con2(int u)
44 {
45 int v;
46 struct node *p;
47 for(p=list[u];p;p=p->next)
48 {
49 v=p->v;
50 if(vis[v])
51 {
52 if(father[u]!=v) low[u]=MIN(low[u],dfn[v]);
53 }
54 else
55 {
56 vis[v]=1,t++;
57 father[v]=u;
58 dfn[v]=low[v]=t;
59 dfs_con2(v);
60 low[u]=MIN(low[u],low[v]);
61 if(dfn[u]>=low[v]) union_set(u,v);
62 }
63 }
64 }
65 int main()
66 {
67 int i,j,a,b,cnt,d1;
68 struct node *q;
69 while(scanf("%d%d",&n,&m)!=EOF)
70 {
71 SET(flag,0);
72 for(i=0;i<m;i++)
73 {
74 scanf("%d%d",&a,&b),a--,b--;
75 if(flag[a][b] || flag[b][a]) continue;
76 flag[a][b]=flag[b][a]=1;
77 addEdge(a,b),addEdge(b,a);
78 }
79 init();
80 dfs_con2(0);
81 SET(id,-1);
82 for(i=0,cnt=0;i<n;i++)
83 {
84 j=find_set(i);
85 if(id[j]==-1) id[j]=cnt++;
86 id[i]=id[j];
87 }
88 SET(d,0);
89 for(i=0;i<n;i++)
90 {
91 for(q=list[i];q;q=q->next)
92 {
93 j=q->v;
94 if(j<i) continue;
95 if(id[i]!=id[j]) d[id[i]]++,d[id[j]]++;
96 }
97 }
98 for(i=0,d1=0;i<cnt;i++) d1+=(d[i]==1)?1:0;
99 printf("%d\n",(d1+1)/2);
100 for(i=0;i<n;i++)
101 {
102 for(q=list[i];q;)
103 {
104 list[i]=q->next,free(q),q=list[i];
105 }
106 }
107 }
108 return 0;
109 }