Tarjan缩点算法的简单应用

前言

这破题目害老子写了一下午,还好总算写出来了,用到的是Tarjan算法

不懂Tarjan算法的童鞋,强烈安利B站一位UP主的讲解视频,大神请绕路

像我这种蒟蒻只有看视频听人家讲解才能会,自学算法好苦逼,看博客讲解真他妈费劲

 

视频链接在此:轻松掌握Tarjan算法,我一遍就完全懂了,强烈安利哦,

建议学会Tarjan算法再来看此题.

题目如下

原题链接

题目描述

给出N个点,M条边的有向图,对于每个点v,求A(v)A(v)表示从点vv出发,能到达的编号最大的点。

输入格式

第1 行,2 个整数N,M。

接下来M行,每行2个整数U,V,表示边(Ui​,Vi​)。点用1,2,⋯,N编号。

输出格式

N 个整数A(1),A(2),⋯,A(N)。

输入输出样例

输入 #1复制

4 3
1 2
2 4
4 3

输出 #1复制

4 4 3 4

说明/提示

• 对于60% 的数据,1≤N.M≤1e3;

• 对于100% 的数据,1≤N,M≤1e5。

题解分析

拿到这道题,本以为是个简单的DFS遍历图的问题,提交,10分

仔细一想应该是有局部闭环的,好,那就记忆化再加上每次重新搜索,hhh只有60分

看其他大佬的分析,可以反着写,又看到什么狗屁Tarjan.......就去学Tarjan了

 

学完Tarjan后........

发现可以把每个强连通分量当成一个点(即缩点),然后做一个映射,重新构建无联通分量的图....

此时重新构建的图就是之前没考虑局部闭环的情况......

知道以上思路+理论后,写代码就很简单了

AC代码如下,有问题各位直接提

#include<bits/stdc++.h>
using namespace std;

const int MAX = 5e5+5;

int M,N;
vector<int> path[MAX];
vector<int> nwPath[MAX];

//tarjan模板所需的变量
int dfn[MAX],low[MAX];
int timeb;
int inStack[MAX];

//记忆化搜索使用的数组,作为结果保存用于输出
int res[MAX];

//tarjan函数中,用于映射新图中的点所需要的变量
int belong[MAX]; //映射新图,与tcc数组一起发挥作用
int tcc[MAX],countTcc; //记录每个强连通分量的最大结点值


//Tarjan模板
void tarjan(int index,stack<int>& s) {
    dfn[index]=low[index]=timeb++;
    inStack[index]=true;
    s.push(index);
    for (int i = 0; i < path[index].size(); ++i) {
        if(!dfn[path[index][i]]) {
            tarjan(path[index][i],s);
            low[index]=min(low[index],low[path[index][i]]);
        } else if(inStack[path[index][i]]) {
            low[index]=min(low[index],low[path[index][i]]);
        }
    }
    if(low[index]==dfn[index]) {
        //为构建新图,对强连通分量做一个映射
        tcc[countTcc] = 0;
        while (!s.empty()&&s.top()!=index) {
            tcc[countTcc]=max(tcc[countTcc],s.top());
            belong[s.top()]=countTcc;
            inStack[s.top()]=false;
            s.pop();
        }
        belong[s.top()]=countTcc;
        tcc[countTcc]=max(tcc[countTcc],s.top());
        inStack[s.top()]=false;
        s.pop();
        countTcc++;
    }
}

//用映射点构建新图
void reBuild() {
    for (int i = 1; i <= N; ++i) {
        for (int j = 0; j < path[i].size(); ++j) {
            nwPath[belong[i]].push_back(belong[path[i][j]]);
        }
    }
}

//简单的记忆化DFS,千万注意搜索的过程中要使用映射后的新图的点
int dfs(int index) {
    if(res[index]) return res[index];
    res[index]=tcc[index];
    for (int i = 0; i < nwPath[index].size(); ++i) {
        res[index]=max(dfs(nwPath[index][i]),res[index]);
    }
    return res[index];
}

int main() {
    cin >> N >> M;
    while (M--) {
        int src,dst;
        cin >> src >> dst;
        path[src].push_back(dst);
    }
    stack<int> ss;
    timeb=1;
    countTcc=0;
    for (int i = 1; i <= N; ++i) {
        if(!dfn[i]) tarjan(i,ss);
    }
    reBuild();
    for (int i = 1; i <= N; ++i) {
        cout << dfs(belong[i]) << " " ;
    }
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值