菜鸟系列——双连通分量
来源:互联网 发布:sam软件和au 编辑:程序博客网 时间:2024/05/16 14:51
菜鸟就要老老实实重新学起:
双连通分量
就是无向图中的点集满足:任意两点之间能够有不止一条通路。
边双连通分量
就是两点间不含有桥的点集,桥就是满足去除该边之后图不再连通的边,也就是任意两点间有不同的边集可以到达,边连通分量之间可能有桥连接。求解就是直接用求有向图强连通分量的tarjan算法,因为是无向图,深搜时上不能返回父节点就行了。
模版:
#define N 110vector<int>g[N];stack<int>st;// 深度优先搜索访问次序, 能追溯到的最早的次序int dfn[N],low[N];// 检查是否在栈中, 记录每个点在第几号强连通分量里int inStack[N],belong[N];// 索引号,双连通分量个数int index,cnt;int n,m;void init(){ for(int i=0;i<N;i++) g[i].clear(); while(!st.empty())st.pop();memset(dfn, 0, sizeof(dfn));memset(low, 0, sizeof(low));memset(inStack, 0, sizeof(inStack));index = cnt = 1;}void tarjan(int x,int fa){int i;// 刚搜到一个节点时low = dfnlow[x] = dfn[x] = index;index++;st.push(x);inStack[x] = 1;int len = g[x].size();int mark = 0;for(i=0;i<len;i++){ int t=g[x][i]; //无向图双连通分量,mark防重边。 if(!mark && t==fa) { mark=1; continue; }if(!dfn[t]){tarjan(t,x);// 回溯的时候改变当前节点的low值low[x] = min(low[x], low[t]);}// 如果新搜索到的节点已经被搜索过而且现在在栈中else if(inStack[t]){ // 更新当前节点的low值,这里的意思是两个节点之间有一条可达边, // 而前面节点已经在栈中,那么后面的节点就可能和前面的节点在一个联通分量中low[x] = min(low[x], dfn[t]);}}// 最终退回来的时候 low == dfn , 没有节点能将根节点更新,那必然就是根节点if(low[x] == dfn[x]){int temp;// 一直出栈到此节点, 这些元素是一个双联通分量while(!st.empty()){temp = st.top();st.pop();belong[temp] = cnt; // 标记双联通分量 inStack[temp] = 0; if(temp == x) break;}cnt++;}}int solve(){ for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i,i); return cnt;}
点双连通分量
就是内部不含有割点的点集,割点就是满足去除该点之后图不联通点,也就是任意两点间有不同的点集可以到达,两个点联通分量之间可能有割点连接,割点属于两个联通分量。
求解也是通过tarjan深搜,但栈中存放边,将图分成不同的点联通分量也就是块。
模版:
#define N 112345struct node{ int x,y; node(int a=0, int b=0) { x=a;y=b; }}tn;//blocks[]存每个块包含的点,bridge存是桥的边vector<int>g[N],blocks[N];vector<node>bridge;stack<node>st;// 深度优先搜索访问次序, 能追溯到的最早的次序int dfn[N],low[N];bool vis[N];// 索引号,块的个数int index,cnt;int n,m;void init(){ for(int i=0;i<N;i++) g[i].clear(),blocks[i].clear(); bridge.clear(); while(!st.empty())st.pop();memset(dfn, 0, sizeof(dfn));memset(low, 0, sizeof(low));index = cnt = 1;}void judge(int u,int v){ int x,y; node temp; memset(vis,false,sizeof(vis)); while(!st.empty()) { temp = st.top();st.pop(); x=temp.x;y=temp.y; if(!vis[y])blocks[cnt].push_back(y),vis[y]=true; //一直到最后一条树枝边为止,起点并不属于这个双连通分量,如果属于,已在后向边中加入。 if(x==u) break; if(!vis[x])blocks[cnt].push_back(x),vis[x]=true; } cnt++;}void tarjan(int x,int fa){low[x] = dfn[x] = index++;int len = g[x].size();for(int i=0;i<len;i++){ int t=g[x][i]; if(t==fa) continue;if(!dfn[t] && dfn[t]<dfn[x]){ //加入树枝边 st.push(node(x,t));tarjan(t,x);low[x] = min(low[x], low[t]);if(dfn[x]<=low[t]) judge(x,t); if(dfn[x]<low[t]) bridge.push_back(node(x,t));}else if(dfn[t] < dfn[x]) { //加入后向边 st.push(node(x,t));low[x] = min(low[x], dfn[t]); }}}int solve(){ for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i,i); return cnt;}
eg:
POJ3352 Road Construction
http://poj.org/problem?id=3352
题意:
给出各景点之间的路线,要求在修任意一条路时还能够到达所有景点,求要至少多修多少临时的桥。思路:
就是求加入最少的边使得整个图变成双连通图,只要求出所有双连通分量然后缩点连线,使之成为双连通图就行了,
求双连通分量缩点用tarjan算法,之后对于所有的度数为一的点连线即可。答案就是(度数唯一的点+1)/2;
code:
#define N 112345int n,m;int flag,sum,ave,ans,res,len,ans1,ans2;int a[N],b[N];vector<int>g[N];stack<int>st;int dfn[N],low[N];int inStack[N],belong[N];int index,cnt;void init(){ for(int i=0;i<N;i++) g[i].clear(); while(!st.empty())st.pop();memset(dfn, 0, sizeof(dfn));memset(low, 0, sizeof(low));memset(inStack, 0, sizeof(inStack));memset(a,0,sizeof(a));index = cnt = 1;}void tarjan(int x, int fa){int i, a;low[x] = dfn[x] = index;index++;st.push(x);inStack[x] = 1;int len = g[x].size();for(i=0;i<len;i++){ int t=g[x][i]; if(t == fa) continue;if(!dfn[t]){tarjan(t,x);low[x] = min(low[x], low[t]);}else if(inStack[t])low[x] = min(low[x], dfn[t]);}if(low[x] == dfn[x]){int temp;while(!st.empty()){temp = st.top();st.pop();belong[temp] = cnt; inStack[temp] = 0; if(temp == x) break;}cnt++;}}int solve(){ for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i,i); return cnt;}int main(){ int i,j,k,kk,t,x,y,z; while(scanf("%d%d",&n,&m)!=EOF&&n) { init(); for(i=0;i<m;i++) { scanf("%d%d",&x,&y); g[x].push_back(y); g[y].push_back(x); } solve(); for(i=1;i<=n;i++) for(j=0;j<g[i].size();j++) if(belong[i]!=belong[g[i][j]]) a[belong[i]]++,a[belong[g[i][j]]]++; sum=0; for(i=1;i<cnt;i++) if(a[i]==2) sum++; sum++; printf("%d\n",sum/2); } return 0;}
POJ3177 Redundant Pathsac
http://poj.org/problem?id=3177
题意:
n个农场,要求彼此之间至少有两条路线,求要新建多少路。思路:
同上题完全一样,就是这题会有重边,注意加flag标记下。code:
#define N 112345int n,m;int flag,sum,ave,ans,res,len,ans1,ans2;int a[N],b[N];vector<int>g[N];stack<int>st;int dfn[N],low[N];int inStack[N],belong[N];int index,cnt;void init(){ for(int i=0;i<N;i++) g[i].clear(); while(!st.empty())st.pop();memset(dfn, 0, sizeof(dfn));memset(low, 0, sizeof(low));memset(inStack, 0, sizeof(inStack));memset(a,0,sizeof(a));index = cnt = 1;}void tarjan(int x, int fa){int i, a;low[x] = dfn[x] = index;index++;st.push(x);inStack[x] = 1;int len = g[x].size();flag=0;for(i=0;i<len;i++){ int t=g[x][i]; if(t == fa && !flag) { flag=1; continue; }if(!dfn[t]){tarjan(t,x);low[x] = min(low[x], low[t]);}else if(inStack[t])low[x] = min(low[x], dfn[t]);}if(low[x] == dfn[x]){int temp;while(!st.empty()){temp = st.top();st.pop();belong[temp] = cnt; inStack[temp] = 0; if(temp == x) break;}cnt++;}}int solve(){ for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i,i); return cnt;}int main(){ int i,j,k,kk,t,x,y,z; while(scanf("%d%d",&n,&m)!=EOF&&n) { init(); for(i=0;i<m;i++) { scanf("%d%d",&x,&y); g[x].push_back(y); g[y].push_back(x); } solve(); for(i=1;i<=n;i++) for(j=0;j<g[i].size();j++) if(belong[i]!=belong[g[i][j]]) a[belong[i]]++,a[belong[g[i][j]]]++; sum=0; for(i=1;i<cnt;i++) if(a[i]==2) sum++; sum++; printf("%d\n",sum/2); } return 0;}
0 0
- 菜鸟系列——双连通分量
- 双连通分量入门——UVALive
- HDU3394.Railway——点双连通分量
- POJ3352.Road Construction——边-双连通分量
- ZOJ2588.Burning Bridges——边双连通分量,桥
- 无向图——双连通分量
- poj3177——Redundant Paths(双连通分量)
- poj3352——Road Construction(双连通分量)
- poj3177——Redundant Paths(双连通分量)
- *无向图求割点+点双连通分量——Tarjan
- *无向图求桥+边双连通分量——Tarjan
- 边双连通分量——学习(复习)笔记
- [边双]hihocoder 1184——边的双连通分量
- uva 10972 边—双连通分量
- 双连通分量
- 双连通分量_road
- 边双连通分量
- 双连通分量
- java 排序算法---插入排序
- The Factor(hdu5428)
- Android的AsyncTask和数据存储
- 重新认识二级指针(Pointers to Pointers)
- lintcode-验证查找二叉树-95
- 菜鸟系列——双连通分量
- list.h 解析
- Urimoo做试卷
- 什么是J2EE
- 重启oracle数据库
- 用busybox搭建最简单的Linux文件系统
- Android关于Theme.AppCompat相关问题的深入分析
- Service IntentService Android中的线程 MediaPlayer
- android 运行时找不到id异常