如果不知道什么双连通: 双连通分量

这题和以前的双连通有点不太一样,以前求得时候都是整个图的割边,这里求得是两点间的割边,结题报告上说了一种暴力的方法,首先求出所有割边,然后求一次最短路,则割边和最短路径求一次交集,可以得到S, T间的桥,很不错的思路,如果没想法了,确实可以暴力一下,这题还可以用dfs + 记录的方法,直接求得S, T间的桥,dfs的时候,在每个桥边(u, v)都需要回溯,判断一下,如果点v能到达了T,则此边必是必经之路,因为dfs是从源点S开始搜的,证明S能到v,而(u, v)是桥,v又能到达T,则可以断定边(u, v)即为所求,具体实现见代码。

 

代码
1 /*求两点间的割边,按升序输出,或叫必经之路上的边*/
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<string.h>
5  #define NN 10004
6 #define MM 200010
7 int idx; // 边数
8 int bCnt;// 连通分支个个数 branch cnt
9 int time;// 记录时间戳
10 int low[NN]; // Low(u)为u或u的子树中能通过非父子边追溯到的最早的节点,即DFS序号最小的节点
11 int dfn[NN]; // 深度优先标号
12 int bel[NN]; // bel[u] = k,节点u属于分支k
13 int mark[NN];
14 int stk[MM];
15 int N, M, top;
16 typedef struct node{
17 int v;
18 int vis;
19 int id;
20 struct node *nxt, *op;
21 }NODE;
22
23 NODE edg[MM];
24 NODE *link[NN];
25 NODE *par[NN]; // 记录到达顶点v的树边
26
27 int Min(int a, int b){
28 return a < b ? a : b;
29 }
30
31 int cmp(const void *a, const void *b){
32 return *(int *)a - *(int *)b;
33 }
34 void Add(int u, int v, int id){ // 加边
35 idx++;
36 edg[idx].v = v;
37 edg[idx].vis = 0;
38 edg[idx].nxt = link[u];
39 link[u] = edg + idx;
40 edg[idx].op = edg + idx + 1; // 表示反向边
41 edg[idx].id = id;
42 idx++;
43 edg[idx].v = u;
44 edg[idx].vis = 0;
45 edg[idx].nxt = link[v];
46 link[v] = edg + idx;
47 edg[idx].op = edg + idx - 1;
48 edg[idx].id = id;
49 }
50
51 int dfs(int u){// 1表示可以到达点N - 1,0表示不可到达
52 int v, tmp;
53 dfn[u] = low[u] = ++time;
54 int ok = 0;
55 if (u == N - 1){
56 ok = 1;
57 }
58 for (NODE *p = link[u]; p; p = p->nxt){
59 if (p->vis) continue;
60 p->vis = p->op->vis = 1; // 由于加的是双向边,所以正向边访问后,反向边也要标记
61 v = p->v;
62 tmp = 0;
63 if (!dfn[v]){
64 par[v] = p->op; // 由于dfs作用是建一棵深搜树,所以每个点的前边是是唯一的
65 tmp = dfs(v);
66 low[u] = Min(low[u], low[v]);
67 }else{
68 low[u] = Min(low[u], dfn[v]);
69 }
70 if (tmp){// 如果v点可以到达终点,且u,v为割边的话,一定是0到N-1的必经之路
71 if(dfn[u] < low[v])
72 stk[++top] = p->id;
73 ok = 1;
74 }
75 }
76 return ok;
77 }
78 void Solve(){
79 int i;
80 top = 0;
81 memset(dfn, 0, sizeof(dfn));
82 dfs(0);
83 if (top == 0){
84 puts("0");
85 puts("");
86 return;
87 }
88 qsort(stk + 1, top, sizeof(stk[0]), cmp);
89 printf("%d\n", top);
90 for (i = 1; i < top; i++) printf("%d ", stk[i]);
91 printf("%d\n", stk[i]);
92 }
93 int main()
94 {
95 int a, b, i;
96 while(scanf("%d%d", &N, &M) != EOF){
97 idx = 0;
98 memset(link, 0, sizeof(link));
99 for(i = 0; i < M; i++){
100 scanf("%d%d", &a, &b);
101 Add(a, b, i);// 将边的标号也记录进去
102 }
103 Solve();
104 }
105 return 0;
106 }
107
posted on 2010-08-23 20:53  ylfdrib  阅读(516)  评论(0编辑  收藏  举报