求图的割点,割边(啊哈算法)

     时间戳:深度优先遍历时,访问每个节点的先后顺序从 1到n;
    使用 num[] 来记录每个顶点的先后顺序;
    满足割点的条件:子节点 v 不经过父节点 u 是不能达到u的祖宗节点的(也就是已经被访问
    的那些节点);
    使用 low[] 来记录每个节点不经过父节点能到达的最早顶点的时间戳;
    因此满足割点的结点是: low[v] >= num[u] ;意思是子节点v不经过父节点u能到达的最早时间
    戳也不过是u,那么把u去掉,不就能把图分成两部分了;但是这也仅仅是来检测非根节点的方法,
    根节点是否为割点还要特判,如果根节点的子节点有两个孩子,且两个孩子不能相互到达,那么
    根节点就是割点;

/**
    时间戳:深度优先遍历时,访问每个节点的先后顺序从 1到n;
    使用 num[] 来记录每个顶点的先后顺序;
    满足割点的条件:子节点 v 不经过父节点 u 是不能达到u的祖宗节点的(也就是已经被访问
    的那些节点);
    使用 low[] 来记录每个节点不经过父节点能到达的最早顶点的时间戳;
    因此满足割点的结点是: low[v] >= num[u] ;意思是子节点v不经过父节点u能到达的最早时间
    戳也不过是u,那么把u去掉,不就能把图分成两部分了;但是这也仅仅是来检测非根节点的方法,
    根节点是否为割点还要特判,如果根节点的子节点有两个孩子,且两个孩子不能相互到达,那么
    根节点就是割点;
*/

/**
    data:
    6 7
    1 4
    1 3
    4 2
    3 2
    2 5
    2 6
    5 6
*/

/**
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int maxn = 1e5+10;
vector<int> Adj[maxn];  ///邻接表
int num[maxn] , low[maxn] ; ///时间戳
int flag[maxn]; ///顶点编号是割点则记为1,否则为0;
int root , index; ///root为根节点,index为时间戳

void dfs(int u,int fath)    ///fath为u的父节点
{
    int child = 0;  ///u的子节点数目
    ++index;    ///时间戳加一
    num[u] = index;
    low[u] = index; ///最开始时间戳就是自己
    for(int i=0;i<Adj[u].size();++i)
    {
        int v = Adj[u][i];
        if(num[v] == 0) ///如果v还没被访问过
        {
            ++child;    ///将v归为u的子节点,子节点数目加一
            dfs(v,u);   ///递归
            low[u] = min(low[u] , low[v]);  ///要将u的时间戳(是low,不是num)更新
            if(u != root && low[v] >= num[u]) ///如果u不是根节点,并且满足割点的条件
                flag[u] = 1;

            ///如果u是根节点且孩子节点也有两个,那么根节点就是割点;
            ///并且我们能证明出两个孩子节点一定不能通过若干结点中转相互到达;
            ///假设能相互到达,那么这两个孩子节点一定不能共同车成为u的孩子节点,
            ///因为一个孩子节点进行递归以后,会把该孩子节点能够连通且没有被访问的结点
            ///进行访问;
            if(u == root && child == 2)
                flag[u] = 1;
        }

        ///如果v已经被访问过且是u的祖宗节点(并不是父节点),则更新当前节点 u 能否
        ///访问到最早顶点的时间戳;
        else if(v != fath)
            low[u] = min(low[u] , num[v]);
    }
}

int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=0;i<m;++i)
    {
        int u,v;
        cin >> u >> v;
        Adj[u].push_back(v);
        Adj[v].push_back(u);
    }

    root = 1;
    dfs(1,root);
    for(int i=1;i<=n;++i)
        if(flag[i] == 1)
            cout << i << endl;
    return 0;
}
*/


2)割边:
    满足割边的条件:子节点 v 不经过父节点 u 是不能达到u的祖宗节点的(也就是已经被访问
    的那些节点);并且不能经过其点到达父节点;即:low[v] > num[u];


/**
2)割边:
    满足割边的条件:子节点 v 不经过父节点 u 是不能达到u的祖宗节点的(也就是已经被访问
    的那些节点);并且不能经过其点到达父节点;即:low[v] > num[u];
*/

/**
    dtta:
    6 6
    1 4
    1 3
    4 2
    3 2
    2 5
    5 6
*/

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int maxn = 1e5+10;
vector<int> Adj[maxn];  ///邻接表
int num[maxn] , low[maxn] ; ///时间戳、
int root , index; ///root为根节点,index为时间戳

void dfs(int u,int fath)    ///fath为u的父节点
{
    ++index;    ///时间戳加一
    num[u] = index;
    low[u] = index; ///最开始时间戳就是自己
    for(int i=0;i<Adj[u].size();++i)
    {
        int v = Adj[u][i];
        if(num[v] == 0) ///如果v还没被访问过
        {
            dfs(v,u);   ///递归
            low[u] = min(low[u] , low[v]);  ///要将u的时间戳(是low,不是num)更新
            if(low[v] > num[u]) ///如果满足割边的条件
                printf("%d---->%d\n",u,v);
        }

        ///如果v已经被访问过且是u的祖宗节点(并不是父节点),则更新当前节点 u 能否
        ///访问到最早顶点的时间戳;
        else if(v != fath)
            low[u] = min(low[u] , num[v]);
    }
}

int main()
{
    int n,m;
    cin >> n >> m;
    for(int i=0;i<m;++i)
    {
        int u,v;
        cin >> u >> v;
        Adj[u].push_back(v);
        Adj[v].push_back(u);
    }

    root = 1;
    dfs(1,root);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
拓扑排序、点与边以及强连通分量是论中的重要概念和算法。 1. 拓扑排序(Topological Sorting): 拓扑排序是对有向无环(DAG)进行排序的一种算法。拓扑排序可以得到一个顶点的线性序列,使得对于任意一条有向边(u, v),在序列中顶点u都排在顶点v的前面。拓扑排序常用于表示任务之间的依赖关系,例如在工程项目中确定任务的执行顺序。 2. 点与边(Cut Vertex and Cut Edge): 点是指在无向连通中,如果移除该顶点以及与该顶点相连的所有边,会导致不再连通,则该顶点被称为点。边是指在无向连通中,如果移除该边,会导致不再连通,则该边被称为边。点和边的存在可以影响到的连通性,因此在网络设计、通信等领域有着重要的应用。 3. 强连通分量(Strongly Connected Component): 强连通分量是指在有向中,如果对于中任意两个顶点u和v,存在从u到v和从v到u的路径,那么称u和v在同一个强连通分量中。强连通分量可以将有向的顶点划分成若干个子集,每个子集内的顶点之间互相可达。强连通分量可以用于分析网络中的关键节点,寻找网络的可靠性,以及在编译器设计中进行代码优化等领域。 这些概念和算法论中都有着广泛的应用,并且还有许多相关的算法和扩展。深入理解和掌握这些概念和算法,可以帮助我们更好地理解和解决各种与相关的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值