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]) << " " ;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章