Skip to content

有向图的完全可达性

题目描述

给定一个有向图,包含 N 个节点,节点编号分别为 1,2,...,N。现从 1 号节点开始,如果可以从 1 号节点的边可以到达任何节点,则输出 1,否则输出 -1。

题目链接:https://kamacoder.com/problempage.php?pid=1177

文章讲解:https://programmercarl.com/kamacoder/0105.有向图的完全可达性.html

思路

目标是判断图是否强连通(从任意节点都能到达任意其他节点)。这里进行了简化,只检查从节点1能否到达所有其他节点。

因为节点编号都从1开始,因此选择K+1、 N+1大小

C++
vector<list<int>> grah(N+1);  // 邻接表存储图
vector<bool> visit(N + 1, false);  // 访问标记数组
构建图
C++
vector<list<int>> grah(N+1);
for(int i = 0;i < K;i++){
    cin >> s >> t;
    grah[s].push_back(t);  // 添加有向边s->t
}
连通性检查
C++
for(int i = 1;i<visit.size();i++){
    if(visit[i] == false) {
        cout << -1 << endl;  // 存在未访问节点,非强连通
        return 0;
    }
}
cout << 1 << endl;  // 所有节点都被访问,强连通

这道题里面卡尔强调了单层递归的两种处理逻辑。和之前思考的问题是一致的。

在递归中,我们是处理当前访问的节点,还是处理下一个要访问的节点?

本题中处理,就是 visit 数组来记录访问过的节点,该节点默认 数组里元素都是false,把元素标记为 true 就是处理 本节点了。

如果我们是处理当前访问的节点,当前访问的节点如果是 true ,说明是访问过的节点,那就终止本层递归,如果不是true,我们就把它赋值为true,因为这是我们处理本层递归的节点。

这种就是先不管下个节点访没访问过,我先递归进去,进去了再看看有没有访问过,如果已经访问过,那我直接返回,退出当前函数就行了。如果没有访问过,那我就处理当前节点,标记为访问过。

接下来我写的这种是我先看看下一个节点访没访问过,访问过我就不递归进去,没访问过,我就处理这个下个节点(标记访问过),然后递归进去。

这种保证了只要在递归内,当前节点一定是已经标记过的。

代码实现

C++
#include <iostream>
#include <vector>
#include <list>
using namespace std;

void dfs(vector<list<int>>& grah,vector<bool>& visit,int key){
    list<int> keys = grah[key];
    for(auto k:keys){
        if(visit[k] == true) continue;
        visit[k] = true;
        dfs(grah,visit,k);
    }
}

int main(){
    int N,K,s,t;
    cin >> N >> K;
    vector<list<int>> grah(N+1);
    for(int i = 0;i < K;i++){
        cin >> s >> t;
        grah[s].push_back(t);
    }

    vector<bool> visit(N + 1, false);

    visit[1] = true;
    dfs(grah,visit,1);
    for(int i = 1;i<visit.size();i++){
        if(visit[i] == false) {
            cout << -1 << endl;
            return 0;
        }
    }
    cout << 1 << endl;

}