写了两天终于写出来了。一开始写的时候卡在了邻接表矩阵的dfs上,今天上午看了一下参考,瞬间感觉自己2了,然后就写出来了用korasaju求强连通分量。又开始卡了,琢磨要是有两个初度为零的强连通的分量怎么办呢?然后瞬间又灰了,最后问我旁边的一哥们怎么解决的,这次发现自己连题意都没读懂,额。。感觉收获颇多。
下面是我花费了很长时间对korasaju算法的证明,及以自己的在学习korasaju算法的时候一些小的学习总结:
首先证明一些这句话: 逆图中能根据u搜到的点v,说明原图中v可以到达u,而原图中v一定是u树中的结点,也就是说u可到达v,从而一定能形成强连通分支。
a) 证明:逆图中能根据u搜到的点v,说明原图中v可以到达u。这个很容易证明,因为你在逆图中u能搜到的点v,原图中肯定是v可以到达u。
b) 证明:而原图中v一定是u树中的结点,也就是说u可到达v。
因为是从u开始搜到v,所以u的结束时间比v的大。
假设原图中u不指向v,又v的结束时间比u小说明先遍历的v后遍历的u,又原图中v可以到达u,故v的结束时间比u的小。则u的结束时间应该比v的小,与已知矛盾,假设不成立。故原图中u能到达v。
综上,也就是说u可到达v,从而形成一个强连通分支。但是能把所有的强连通分支都遍历出来吗?根据强连通性的特点,只要u,v为强连通分量,不管逆图还是原图中u和v都能互相到达。结合上面的证明两个节点的推广一下,就证明了能把包含起始节点u的极大强连通分量给遍历出来。
参考链接:http://www.cnblogs.com/rainydays/archive/2011/05/01/2033941.html
自己的一些总结。
根据深搜和回溯来标示节点的开始与结束时间故可以推出以下的性质:
1.所有比一个结点X的结束时间小的节点都是在原图中节点X能到达的节点
2.如果逆图中X能到达某一个节点Y,说明原图中节点Y能到达X.
3.节点X结束时间最大说明:X是一个子树的根,否则X的结束时间不是最大的,其根的时间要比它还大。
4.其所有子树节点的值处在其开始时间和结束时间之间。
5.所有比节点X的结束时间大的节点是节点X不能到达的,所以先处理大的结束时间就避免了转置图中所有X能到达的没被访问过的节点在原图中是X能到
的。要是X不能到肯定结束时间比X的大已经被处理。
#include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct node{ int vertext; struct node *next_vertext; }EDGE; typedef struct{ int begin_time; int end_time; int num; int color; int popular_rate; }cow; EDGE edge[10010],*current_node[10010],edge_invers[10010],*current_node_inverse[10010]; cow c[10010]; int n,flag[10010]; int begin_end_time; int hash[10010]; int color,color_num[10010]; void insert_me(int s,int d) { current_node[s]->next_vertext = (EDGE*)malloc(sizeof(EDGE)); current_node[s] = current_node[s]->next_vertext; current_node[s]->vertext = d; current_node[s]->next_vertext = NULL; return; } void build_invers(int s,int d) { current_node_inverse[s]->next_vertext = (EDGE*)malloc(sizeof(EDGE)); current_node_inverse[s] = current_node_inverse[s]->next_vertext; current_node_inverse[s]->vertext = d; current_node_inverse[s]->next_vertext = NULL; return; } void init(int init_edge){ for(int i = 1;i <=n;++i){ current_node[i] = &edge[i]; current_node_inverse[i] = &edge_invers[i]; } if(init_edge){ for(int i = 1;i <= n; ++i){ edge[i].vertext = i; edge[i].next_vertext = NULL; edge_invers[i].vertext= i; edge[i].next_vertext = NULL; } } return ; } //遍历邻接表 void dfs(int start) { flag[start] = 1; c[start].begin_time = begin_end_time++; EDGE *p = edge[start].next_vertext; c[start].num = start; while(p != NULL){ if(!flag[p->vertext]) dfs(p->vertext); p = p->next_vertext; } c[start].end_time = begin_end_time++; return; } void dfs_invers(int start) { flag[start] = 1; EDGE *p = edge_invers[start].next_vertext; c[hash[start]].color = color; ++color_num[color]; while(p != NULL) { if(!flag[p->vertext]) dfs_invers(p->vertext); p = p->next_vertext; } return; } int cmp(const void *a,const void *b) { return ((cow*)a) -> end_time - ((cow*)b) -> end_time; } void kosaraju(){ begin_end_time = 0; color = 0; memset(color_num,0,sizeof(color_num)); memset(flag,0,sizeof(flag)); for(int i = 1; i<=n; ++i) { if(!flag[i]) dfs(i); } qsort(&c[1],n,sizeof(c[0]),cmp); memset(flag,0,sizeof(flag)); //下标变换 for(int i = 1;i <=n;++i) { hash[c[i].num] = i; } for(int i = n;i>0;--i) { if(!flag[c[i].num]){ dfs_invers(c[i].num); ++color; } } } int main(){ int m; scanf("%d%d",&n,&m); int fr,to; init(1); for(int i = 0;i <m; ++i){ scanf("%d%d",&fr,&to); insert_me(fr,to); build_invers(to,fr); } init(0); kosaraju(); //who popular if(color == 1){ printf("%d\n",color_num[0]); }else{ memset(flag,0,sizeof(flag)); EDGE *p; for(int i = 1;i<=n;++i) { if(flag[c[hash[i]].color] == 0) { p = edge[i].next_vertext; while(p!=NULL) { if(c[hash[p->vertext]].color != c[hash[i]].color) { flag[c[hash[i]].color] = 1; break; } p = p->next_vertext; } } } int min = -1; for(int i = 0;i<color;++i){ if(flag[i] == 0){ if(min == -1){ min = i; }else{ min = -2; break; } } } if(min != -2){ printf("%d\n",color_num[min]); }else{ printf("%d\n",0); } } // for(int i = 1;i <=n;++i) // { // printf("num:%d color:%d\n",c[i].num,c[i].color ); // } system("pause"); }