Tarjan系列1
Posted Der Barde, Nietzsche
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tarjan系列1相关的知识,希望对你有一定的参考价值。
tajan的dfs树系列算法:
求解割点,桥,强连通分量,点双联通分量,边双联通分量;
tajan是一个dfs,把一个图变成一个dfs树结构,
dfs树结构,本质是通过一个没有任何要求的dfs把图的边分为:树边和返祖边:
- 树边:dfs中父节点与其未曾遍历过的子节点间的边,
- 返祖边:父节点与他的dfs中曾作为该父节点祖先的子节点间的边
在有向图中,除了这二种边外,还有父节点与曾遍历过的子节点间的边,然而这个子节点不是父节点的祖先,
然而这种边在tarjan中没有意义,我们所求的东西用不上她们
伪代码:
深搜(点now){
更新点now——
dfs序(dfn)与目前可到dfn最小祖先的dfn(low),标记已经遍历(vis),确认now将是他后继递归的点的祖先(instk),其他
for(以now为起点的所有边)
if(边终点to未遍历)
深搜(to),low[now]=min(low[now],low[to])
else
if(instk[to]为真)//无向图可以不存在这个
low[now]=min(low[now],dfn[to])
更新instk[now]为假
}
求(无向图)割点,桥:
割点:无向图中,删除之可改变图的连通性的点
两种:
- 两个点双连通分量的公共点
- 两个点双连通分量直接存在一条边连接,这条边的两端点
桥:无向图中,删除之可改变图的连通性的边
一种:
两个边双连通分量间的连边;
求法:
割点:这个点儿子中有至少一个的low小于这个点的dfn
桥:该边终点的low=dfn
例题:
code:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m; struct ss{ int to,next; }e[200010]; struct Cl{ int u,v; }Cedge[200010]; int first[20010],num; int pnu,enu; int Cpoint[20010]; int dfn[20010],low[20010],vis[20010]; bool cmp(Cl a,Cl b){ return a.u<b.u||(a.u==b.u&&a.v<b.v); } void Input(); void work(); void Output(); void build(int ,int ); void Init(); void dfs_1(int ,int ); void dfs_2(int ,int ); int main() { Input(); work(); Output(); return 0; } void Input(){ int i,j,k; scanf("%d%d",&n,&m); for(i=1;i<=m;i++){ scanf("%d%d",&j,&k); build(j,k);build(k,j); } } void work(){ pnu=0;enu=0; Init(); dfs_1(1,0); Init(); dfs_2(1,0); } void Output(){ int i; for(i=1;i<=enu;i++) if(Cedge[i].u>Cedge[i].v) swap(Cedge[i].u,Cedge[i].v); sort(Cpoint+1,Cpoint+pnu+1); sort(Cedge+1,Cedge+enu+1,cmp); for(i=1;i<=pnu;i++) printf("%d ",Cpoint[i]); if(pnu==0) printf("Null"); printf("\n"); for(i=1;i<=enu;i++) printf("%d %d\n",Cedge[i].u,Cedge[i].v); } void build(int f,int t){ e[++num].next=first[f]; e[num].to=t; first[f]=num; } void Init(){ memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(vis,0,sizeof(vis)); num=0; } void dfs_1(int now,int fa){ int i,p=0; if(now!=1)p=1; dfn[now]=low[now]=++num;vis[now]=1; for(i=first[now];i;i=e[i].next) if(e[i].to!=fa){ if(!vis[e[i].to]){ dfs_1(e[i].to,now); if(low[e[i].to]>=dfn[now])p++; if(low[now]>low[e[i].to]) low[now]=low[e[i].to]; } else if(low[now]>dfn[e[i].to]) low[now]=dfn[e[i].to]; } if(p>=2) Cpoint[++pnu]=now; } void dfs_2(int now,int fa){ int i; dfn[now]=low[now]=++num;vis[now]=1; for(i=first[now];i;i=e[i].next) if(e[i].to!=fa){ if(!vis[e[i].to]){ dfs_2(e[i].to,now); if(low[now]>low[e[i].to]) low[now]=low[e[i].to]; if(low[e[i].to]>dfn[now]) Cedge[++enu].u=now,Cedge[enu].v=e[i].to; } else if(low[now]>dfn[e[i].to]) low[now]=dfn[e[i].to]; } }
求(有向图)强连通分量:
强联通分量:有向图中,点的可以相互到达的关系可以传递,于是有这个关系的一组点与其间的边构成一个强连通分量
求法:
每遍历一个点时,使之进栈,
当遍历结束时,
若其low=dfn,则从栈顶到该节点的所有点属于同一分量,且该分量不含其它点,标记她们,并使她们出栈
(now永远不能到达上层的点,于是她与她的子树中没有自成一派的点构成强连通分量,自成一派的点已经出栈了)
例题:
强连通分量缩点后讨论无出度点的个数
code:
#include<cstdio> using namespace std; struct ss{ int to,next; }e[2000010]; int first[200010],num; int dfn[100010],low[100010],vis[200010],stk[100010],col[100010],number; int numcol[200010],into[100],color; int max[200010]; long long f[200010],x; int n,m; void Input(); void work(); void Output(); void build(int ,int ); void tar(int ); void con_poi(); void dfs(int ); int main() { Input(); work(); Output(); return 0; } void Input(){ int i,j,k; scanf("%d%d%lld",&n,&m,&x); for(i=1;i<=m;i++){ scanf("%d%d",&j,&k); build(j,k); } } void work(){ int i,j; color=n; for(i=1;i<=n;i++) if(!vis[i]) number=0,tar(i); con_poi(); for(i=n+1;i<=color;i++) if(!vis[i]) dfs(i); } void Output(){ int i; long long ans=0,ans_=0; for(i=n+1;i<=color;i++){ if(max[i]==ans) (ans_+=f[i])%=x; if(max[i]>ans) ans=max[i],ans_=f[i]; } printf("%lld\n%lld\n",ans,ans_); } void build(int f,int t){ e[++num].next=first[f]; e[num].to=t; first[f]=num; } void tar(int now){ int i; dfn[now]=low[now]=++number; vis[now]=2;stk[++stk[0]]=now; for(i=first[now];i;i=e[i].next) if(!vis[e[i].to]){ tar(e[i].to); if(low[now]>low[e[i].to]) low[now]=low[e[i].to]; } else if(vis[e[i].to]==2&&low[now]>dfn[e[i].to]) low[now]=dfn[e[i].to]; if(dfn[now]==low[now]){ ++color; while(stk[stk[0]+1]!=now){ col[stk[stk[0]]]=color; vis[stk[stk[0]]]=1; ++numcol[color]; --stk[0]; } number=stk[0]; } } void con_poi(){ int i,j; for(i=1;i<=n;i++) for(j=first[i];j;j=e[j].next) if(col[i]!=col[e[j].to]) build(col[i],col[e[j].to]),into[col[e[j].to]]++; } void dfs(int now){ int i; f[now]=1;max[now]=numcol[now]; for(i=first[now];i;i=e[i].next) if(!vis[e[i].to]){ vis[e[i].to]=1; if(!f[e[i].to]) dfs(e[i].to); if(max[now]==max[e[i].to]+numcol[now]) (f[now]+=f[e[i].to])%=x; if(max[now]<max[e[i].to]+numcol[now]){ max[now]=max[e[i].to]+numcol[now]; f[now]=f[e[i].to]; } } for(i=first[now];i;i=e[i].next) if(vis[e[i].to]) vis[e[i].to]=0; }
强连通分量缩点,计算最长路和最长路计数
code:
#include<cstdio> using namespace std; struct ss{ int to,next; }e[2000010]; int first[200010],num; int dfn[100010],low[100010],vis[200010],stk[100010],col[100010],number; int numcol[200010],into[100],color; int max[200010]; long long f[200010],x; int n,m; void Input(); void work(); void Output(); void build(int ,int ); void tar(int ); void con_poi(); void dfs(int ); int main() { Input(); work(); Output(); return 0; } void Input(){ int i,j,k; scanf("%d%d%lld",&n,&m,&x); for(i=1;i<=m;i++){ scanf("%d%d",&j,&k); build(j,k); } } void work(){ int i,j; color=n; for(i=1;i<=n;i++) if(!vis[i]) number=0,tar(i); con_poi(); for(i=n+1;i<=color;i++) if(!vis[i]) dfs(i); } void Output(){ int i; long long ans=0,ans_=0; for(i=n+1;i<=color;i++){ if(max[i]==ans) ans_+=f[i]; if(max[i]>ans) ans=max[i],ans_=f[i]; } printf("%lld\n%lld\n",ans,ans_); } void build(int f,int t){ e[++num].next=first[f]; e[num].to=t; first[f]=num; } void tar(int now){ int i; dfn[now]=low[now]=++number; vis[now]=2;stk[++stk[0]]=now; for(i=first[now];i;i=e[i].next) if(!vis[e[i].to]){ tar(e[i].to); if(low[now]>low[e[i].to]) low[now]=low[e[i].to]; } else if(vis[e[i].to]==2&&low[now]>dfn[e[i].to]) low[now]=dfn[e[i].to]; if(dfn[now]==low[now]){ ++color; while(stk[stk[0]+1]!=now){ col[stk[stk[0]]]=color; vis[stk[stk[0]]]=1; ++numcol[color]; --stk[0]; } number=stk[0]; } } void con_poi(){ int i,j; for(i=1;i<=n;i++) for(j=first[i];j;j=e[j].next) if(col[i]!=col[e[j].to]) build(col[i],col[e[j].to]),into[col[e[j].to]]++; } void dfs(int now){ int i; f[now]=1;max[now]=numcol[now]; for(i=first[now];i;i=e[i].next) if(!vis[e[i].to]){ vis[e[i].to]=1; if(!f[e[i].to]) dfs(e[i].to); if(max[now]==max[e[i].to]+numcol[now]) (f[now]+=f[e[i].to])%=x; if(max[now]<max[e[i].to]+numcol[now]){ max[now]=max[e[i].to]+numcol[now]; f[now]=f[e[i].to]; } } for(i=first[now];i;i=e[i].next) if(vis[e[i].to]) vis[e[i].to]=0; }
强连通分量缩点,乱搞,具体看代码;
code:
#include<cstdio> #include<cstring> using namespace std; int n,m,p,ans; struct ss{ int to,next; }e[600010]; int first[200010],num; int dfn[100010],low[100010],vis[200010],stk[200010],number; int col[100010],color,numcol[200010]; int toit[200010]; void Input(); void work(); void Output(); void build(int ,int ); void tar(int ); void dfs_1(int ); int main() { Input(); work(); Output(); return 0; } void Input(){ int i,j,k; scanf("%d%d",&n,&m); for(i=1;i<=m;i++){ scanf("%d%d",&j,&k); build(j,k); } } void work(){ int i,j,k; color=n; for(i=1;i<=n;i++) if(!vis[i]) number=0,tar(i); for(i=1;i<=n;i++) for(j=first[i];j;j=e[j].next) if(col[i]!=col[e[j].to]) build(col[i],col[e[j].to]),toit[col[e[j].to]]++; memset(stk,0,sizeof(stk)); for(i=n+1;i<=color;i++) if(!toit[i]) vis[i]++,dfs_1(i); for(i=n+1;i<=color;i++) if(!toit[i]){ k=(numcol[i]==1); for(j=first[i];j;j=e[j].next) k&=(vis[e[j].to]>1); p|=k; ans++; } } void Output(){ printf("%.6lf",1.0-(double)(ans-p)/(double)(n)); } void build(int f,int t){ e[++num].next=first[f]; e[num].to=t; first[f]=num; } void tar(int now){ int i; dfn[now]=low[now]=++number; stk[++stk[0]]=now;vis[now]=2; for(i=first[now];i;i=e[i].next) if(!vis[e[i].to]){ tar(e[i].to); if(low[now]>low[e[i].to]) low[now]=low[e[i].to]; } else if(vis[e[i].to]==2&&low[now]>dfn[e[i].to]) low[now]=dfn[e[i].to]; if(dfn[now]==low[now]){ ++color; while(stk[stk[0]+1]!=now){ col[stk[stk[0]]]=color; vis[stk[stk[0]]]=1; ++numcol[color]; --stk[0]; } number=stk[0]; } } void dfs_1(int now){ int i; for(i=first[now];i;i=e[i].next) if(!stk[e[i].to]){ stk[e[i].to]=1; vis[e[i].to]++; if(vis[e[i].to]==1) dfs_1(e[i].to); } for(i=first[now];i;i=e[i].next) if(stk[e[i].to]) stk[e[i].to]=0; }
边双点双下次再说吧;
以上是关于Tarjan系列1的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloud系列十一:SpringCloudStream(SpringCloudStream 简介创建消息生产者创建消息消费者自定义消息通道分组与持久化设置 RoutingKey)(代码片段
SpringCloud系列四:Eureka 服务发现框架(定义 Eureka 服务端Eureka 服务信息Eureka 发现管理Eureka 安全配置Eureka-HA(高可用) 机制Eur(代码片段
Azure 机器人微软Azure Bot 编辑器系列 : 机器人/用户提问回答模式,机器人从API获取响应并组织答案 (The Bot Framework Composer tutorial(代码片段