• P3916 图的遍历 题解


    题目传送门

    一、暴力法建图+DFS遍历

    上来就傻啦吧唧的暴力存图,暴力(DFS),信奉大力出奇迹!此代码过了2个测试点,其它(MLE),原因:测试数据有环!不加(st)数组,就死循环了,表现就是(MLE)!

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 100010;
    int n; //n个结点
    int m; //m条边
    int res[N];
    int u, v;
    vector<int> p[N]; //vector存图
    
    /**
     * 深度优先搜索
     * @param x 从哪个号结点出发
     * @param d 已经到达过的最大号结点
     */
    void dfs(int x, int ne) {
        //没走过,更新最大号为d
        res[x] = max(ne, res[x]);
        //遍历所有出边,尝试找到更大号的结点
        for (int i = 0; i < p[ne].size(); i++) dfs(x, p[ne][i]);
    }
    
    int main() {
        //读入
        scanf("%d%d", &n, &m);
    
        //构建图
        for (int i = 1; i <= m; i++) {
            scanf("%d%d", &u, &v);
            p[u].push_back(v); //正向建边,结点u出发有一条到结点v的边
        }
        //逐个深度优先搜索,找出每个结点能够到达的最大号结点
        for (int i = 1; i <= n; i++) dfs(i, i);
    
        //输出
        for (int i = 1; i <= n; i++) printf("%d ", res[i]);
        printf("
    ");
    
        return 0;
    }
    

    二、加上st数组,防止有环

    此代码过了(9)个测试点,(1)个测试点(TLE)!为啥会(TLE)呢?看一下数据范围:(1≤N,M≤10^5),遍历每个结点是(N), 假设第一个结点就有(M)条边,那么它需要执行(M)
    所有节点遍历一遍历就是(N*M)次,时间复杂度就是(1e10)啊,一秒肯定过不了啊!

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 100010;
    int n; //n个结点
    int m; //m条边
    int res[N];
    int u, v;
    vector<int> p[N]; //vector存图
    
    bool st[N];
    
    /**
     * 深度优先搜索
     * @param x  从哪个号结点出发
     * @param ne 到达了哪个结点
     */
    void dfs(int x, int ne) {
        if (!st[ne]) {
            st[ne] = true;
            //没走过,更新最大号为d
            res[x] = max(ne, res[x]);
            //遍历所有出边,尝试找到更大号的结点
            for (int i = 0; i < p[ne].size(); i++) dfs(x, p[ne][i]);
        }
    }
    
    int main() {
        //读入
        scanf("%d%d", &n, &m);
    
        //构建图
        for (int i = 1; i <= m; i++) {
            scanf("%d%d", &u, &v);
            p[u].push_back(v); //正向建边,结点u出发有一条到结点v的边
        }
        //逐个深度优先搜索,找出每个结点能够到达的最大号结点
        for (int i = 1; i <= n; i++) {
            memset(st, false, sizeof st);
            dfs(i, i);
        }
    
        //输出
        for (int i = 1; i <= n; i++) printf("%d ", res[i]);
        printf("
    ");
    
        return 0;
    }
    

    三、逆向思维,反向建图,AC

    基本思想:

    反向建边是逆向思维,这样(dfs)时可以通过打标记来更便捷地记录某点所能到达的最大点,而正向建边(dfs)时可能会重复搜索,遍历整个图,会增大复杂度

    逆向建图时,从大的点到小的点循环,可以保证小的点被大的点覆盖掉,就不必再参加运算了,因为,再算也不会更大了。这样,就不必每次清空(st)数组,很容易就跑出来了。

    感悟:正向建边(TLE)时,考虑一下反向建边

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 100010;
    int n; //n个结点
    int m; //m条边
    int res[N];
    int u, v;
    vector<int> p[N]; //vector存图
    
    /**
     * 功能:填充每个结点能够到达的最大结点号
     * @param x 哪个结点
     * @param d 最大结点号
     */
    void dfs(int x, int d) {
        if (res[x]) return; //访问过
        res[x] = d;
        for (int i = 0; i < p[x].size(); i++) dfs(p[x][i], d);
    }
    
    int main() {
        //读入
        scanf("%d%d", &n, &m);
        //构建图
        for (int i = 1; i <= m; i++) {
            scanf("%d%d", &u, &v);
            p[v].push_back(u); //反向建边
        }
        //从大到小,逐个深度优先搜索
        for (int i = n; i; i--) dfs(i, i);
        //输出每个结点的最大到达结点号
        for (int i = 1; i <= n; i++) printf("%d ", res[i]);
        printf("
    ");
        return 0;
    }
    
  • 相关阅读:
    Permission denied (publickey). fatal: Could not read from remote repository.
    jQuery OCUpload一键上传文件
    org.apache.subversion.javahl.ClientException: Working copy is not up-to-date
    测开之路一百三十五:实现登录身份验证功能
    测开之路一百三十四:实现指定查找功能
    测开之路一百三十三:实现sql函数封装
    测开之路一百三十二:实现修改功能
    测开之路一百三十一:实现删除功能
    测开之路一百三十:实现前端到数据库交互(增和查)
    测开之路一百二十九:jinja2模板语法
  • 原文地址:https://www.cnblogs.com/littlehb/p/15119645.html
Copyright © 2020-2023  润新知