求割点
DFS搜索树:用DFS对图进行遍历时,按照遍历次序的不同,我们可以得到一棵DFS搜索树。
树边:(称为父子边),可理解为在DFS过程中访问未访问节点时所经过的边。
回边:(返祖边、后向边),可理解为在DFS过程中遇到已访问节点时所经过的边。
该算法是R.Tarjan发明的。观察DFS搜索树,我们可以发现有两类节点可以成为割点:
- 对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;
- 对非叶子节点u(非根节点),若其有一子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与u的子树的节点不再连通;则节点u为割点。
http://cogs.pro/cogs/problem/problem.php?pid=8
#include<cstdio> #include<algorithm> using namespace std; struct Edge{ int to,next; }edge[10005]; int fi[105],top,low[105],fa[105],dfn[105],kid[105]; bool vis[105],cut[105]; void add_edge(int from,int to){ edge[++top].next=fi[from],fi[from]=top,edge[top].to=to; edge[++top].next=fi[to],fi[to]=top,edge[top].to=from; } void dfs(int x){ //记录dfs遍历次序 static int counter = 0; dfn[x]=low[x]=++counter; vis[x]=1; if(x==4) x=4; for(int to = fi[x];to;to=edge[to].next){ int v=edge[to].to; if(!vis[v]){//节点v未被访问,则(u,v)为树边 fa[v]=x;kid[x]++; dfs(v); low[x]=min(low[x],low[v]); if(low[v]>=dfn[x])cut[x]=1; } else if(v!=fa[x]){//节点v已访问,则(u,v)为回边 low[x]=min(low[x],dfn[v]); } } } int main(){ freopen("gd.in","r",stdin); freopen("gd.out","w",stdout); int n,a,b,ans=0;scanf("%d",&n); while(~scanf("%d%d",&a,&b))add_edge(a,b); dfs(1); if(kid[1]<2)cut[1]=0; for(int i=1;i<=n;i++){ if(cut[i])ans++; } printf("%d ",ans); for(int i=1;i<=n;i++){ if(cut[i])printf("%d ",i); } return 0; }
求桥
对于一条边(u,v),u为祖先,如果v的所有子树都没有指向比v先遍历到的点则(u,v)为桥