• 云里雾里学Tarjan(强连通分量)


    首先让我先来说一说关于强连通分量的一些疑惑

    1、当我已经知道了我要走的下一步节点在栈中,我能不能把比较中的DFN写成LOW?

    2、如果low[v]>=dfn[u],此时u就是割点,这个东西怎么证明?

    这里链接两篇Tarjan算法写的非常好的博客:

    1、全网最!详!细!tarjan算法讲解

    2、割点(Tarjan算法)

    ↑洛谷有模板

    下面是鄙人写的裸的Tarjan求图中强连通分量的代码!

    //Tarjan基本的算法实现 
    
    #include<bits/stdc++.h>
    using namespace std;
    struct sd{
    	int v,next;
    }edge[1001];
    int DFN[1001],LOW[1001];
    int stk[1001],head[1001],vis[1001],cnt,tot,index,n,m;
    void add(int x,int y)
    {//链式前向星 
    	edge[++cnt].next=head[x];
    	edge[cnt].v=y;
    	head[x]=cnt;return; 
    }
    void tarjan(int x)
    {
    	DFN[x]=LOW[x]=++tot;
    	stk[++index]=x;
    	vis[x]=1;
    	for(int i=head[x];i;i=edge[i].next)
    	{
    		if(!DFN[edge[i].v])
    		tarjan(edge[i].v),LOW[x]=min(LOW[x],LOW[edge[i].v]);
    		else if(vis[edge[i].v])
    		LOW[x]=min(LOW[x],DFN[edge[i].v]);
    	}
    	if(DFN[x]==LOW[x])
    	{
    		while(stk[index]!=x)
    			printf("%d ",stk[index]),vis[stk[index]]=0,index--;
    		printf("%d
    ",stk[index]),vis[stk[index]]=0,index--;
    	}
    } 
    int main()
    {
    	scanf("%d%d",&n,&m);
    	int x,y;
    	for(int i=1;i<=m;++i)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y);
    	}
    	for(int i=1;i<=n;++i)
    	{
    		if(!DFN[i]) tarjan(i);
    	}
    	return 0;
    } 
    /*
    6 8
    1 2
    1 4
    2 3
    3 6
    2 5
    5 6
    4 5
    5 1
    */
    

    下面是我给出的求割点的tarjan算法程序。我还是有一些东西没有搞懂。(带有注释)

    这里给一张图方便理解:(红色的是DFN,蓝色的是LOW)

    例题:P3388 【模板】割点(割顶)

    //tarjan算法求割点,我好像自己也不是很懂
    #include<bits/stdc++.h>
    using namespace std;
    const int N=100010;
    struct sd{
    	int next,v;
    }edge[2*N];
    int head[N],DFN[N],LOW[N],cnt=0,tot=0,n,m; 
    bool judge[N];
    void add(int x,int y)
    {
    	edge[++cnt].next=head[x];
    	edge[cnt].v=y;
    	head[x]=cnt;
    }
    void tarjan(int x,int fa)
    {
    	int child=0;
    	DFN[x]=LOW[x]=++tot;
    	for(int i=head[x];i;i=edge[i].next)
    	{
    		int v=edge[i].v;//v是要到达的点 
    		if(!DFN[v])
    		{
    			tarjan(v,fa);
    			LOW[x]=min(LOW[x],LOW[v]);
    			if(LOW[v]>=DFN[x]&&x!=fa)judge[x]=true;//就是说明下一个点的最早能追溯到的节点比当前点的dfs序大或等于 
    			if(x==fa)child++;//找回来了,说明已经遍历完了一颗子树
    		}
    		LOW[x]=min(LOW[x],DFN[v]);//?? 判断回边因为是无向图!!!这里不能改的过于大了不能改成LOW否则容易出问题! 
    	}
    	if(child>=2&&x==fa)judge[x]=true;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;++i)
    	{
    		int a,b;
    		scanf("%d%d",&a,&b);
    		add(a,b);
    		add(b,a);
    	}
    	for(int i=1;i<=n;++i)
    	if(!DFN[i])tarjan(i,i);
    	int ans=0;
    	for(int i=1;i<=n;++i)
    	if(judge[i])ans++;printf("%d
    ",ans);
    	for(int i=1;i<=n;++i)
    	if(judge[i])printf("%d ",i);
    	return 0;
    }
    

    By njc

  • 相关阅读:
    快速幂
    三角函数与反三角函数的使用
    poj1050 dp动态规划
    归并排序算法
    KMP算法(查找子序列)
    hdu1233 继续畅通工程 (最小生成树——并查集)
    set<pair<int,int> > 的运用
    HDU 4513 吉哥系列故事――完美队形II (manacher算法)
    Codeforces Round #360 (Div. 2) C. NP-Hard Problem (BFS)
    Codeforces Round #357 (Div. 2) C.Heap Operations 优先队列 + 模拟
  • 原文地址:https://www.cnblogs.com/mudrobot/p/13329335.html
Copyright © 2020-2023  润新知