zoukankan      html  css  js  c++  java
  • P2661 [NOIP2015 提高组] 信息传递 题解

    题目传送门

    一、解题思路

    1、用拓扑排序干掉非环结点

    2、用(dfs)或者(bfs)找出最小环的长度

    二、拓扑排序+dfs 解法

    1、vector邻接表实现

    #include <bits/stdc++.h>
    
    using namespace std;
    
    /**
    思路:其实就是求最小环。每个点的出度都是1,因此构成的图要么是一条链+一个环,要么是几个环,
    通过拓扑可以消去链状的部分,对环的部分dfs算最小环即可。
    */
    const int N = 2e5 + 10;
    
    int n;                  //n个同学
    int ans = 0x3f3f3f3f;   //答案,初始值为极大值
    vector<int> edge[N];    //邻接表
    int ind[N];             //入度数组
    queue<int> q;           //拓扑排序用的队列
    bool st[N];             //是不是已经被排除掉
    
    //拓扑排序[这是一个拓扑排序的模板,但里面扩充了本题的st[i]数组标识 ]
    void topsort() {
        //入度为0的结点入队列
        for (int i = 1; i <= n; i++) if (!ind[i]) q.push(i);
        //图的广度优先遍历
        while (!q.empty()) {
            int x = q.front();
            q.pop();
            //标识非环中结点
            st[x] = true;
            //遍历每条出边
            for (int i = 0; i < edge[x].size(); i++) {
                int y = edge[x][i];
                ind[y]--;
                if (!ind[y]) q.push(y);//在删除掉当前结点带来的入度后,是不是入度为0了,如果是将点y入队列
            }
        }
    }
    
    /**
     功能:求图中包含点u的环的长度
     参数: u   结点
           len 长度
     */
    void dfs(int u, int len) {
        //标识探测过了
        st[u] = true;
        //遍历所有u开头的边
        for (int y:edge[u]) {
            //如果这个点还没有被探测过,那么继续探索
            if (!st[y]) dfs(y, ++len);
            else { //到这里是发现了环!因为现在在邻接表中只剩下了环,其它的链路都被拓扑排序干掉了,
                // 结果还是出现访问过的结点,就肯定是发现环了
                ans = min(ans, len); //这是递归的出口.此处和其它的递归不一样啊,递归出口居然在代码区,
                // 而不是在一进递归的位置~
                return;
            }
        }
    }
    
    int main() {
        //创建有向图
        cin >> n;
        for (int i = 1; i <= n; i++) {
            int u;
            cin >> u;
            //邻接表方式
            edge[i].push_back(u);
            //入度++
            ind[u]++;
        }
        //拓扑排序
        topsort();
    
        //到这里,st[i]=false的就应该是环中结点,对环的部分dfs算最小环即可
        for (int i = 1; i <= n; i++) if (!st[i]) dfs(i, 1);
        cout << ans << endl;
    }
    

    2、链式前向星实现

    #include <bits/stdc++.h>
    
    using namespace std;
    const int INF = 0x3f3f3f3f;
    //拓扑排序+链接式前向星实现
    /**
    思路:其实就是求最小环。每个点的出度都是1,因此构成的图要么是一条链+一个环,要么是几个环,通过拓扑可以消去链状的部分,
    对环的部分dfs算最小环即可。
    */
    const int N = 2e5 + 10;
    
    int n, idx, ans = INF;
    int head[N];    //链表头数组
    
    int in[N];      //入度数组
    queue<int> q;   //拓扑排序用的队列
    
    struct Edge {
        int to, next;
    } edge[N]; //边数,也不可能多于结点数,因为这里是指每个结点引出的边数集合
    
    bool st[N];
    
    //从u向v连一条边,本题无权值概念,头插法
    void add(int u, int v) {
        // 进来先++是非常优美的操作,省去了初始化head[i]=-1!~~~,不过注意,遍历的方式有所变动,第二个条件是i,而不是i!=-1
        edge[++idx].to = v;  //因为idx默认值是,进来先++,就是第一个可用值是edge[1],edge[0]丢弃不使用的意思
        edge[idx].next = head[u];
        head[u] = idx;
        //入度++
        in[v]++;
    }
    
    //拓扑排序
    void topsort() {
        //入度为0的结点入队列,进行拓扑排序
        for (int i = 1; i <= n; i++) if (!in[i]) q.push(i);
    
        //拓扑排序
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            //不是环中结点
            st[u] = true;
            //遍历每条出边
            for (int j = head[u]; j; j = edge[j].next) {
                int y = edge[j].to;
                if (!--in[y]) q.push(y);//在删除掉当前结点带来的入度后,是不是入度为0了,如果是将点y入队列
            }
        }
    }
    
    /**
     功能:求DAG中包含点u的环的长度
     参数: u   结点
           len 长度
     */
    void dfs(int u, int len) {
        //标识探测过了
        st[u] = true;
        //遍历所有u开头的边
        for (int i = head[u]; i; i = edge[i].next) {
            int y = edge[i].to;
            //如果这个点还没有被探测过,那么,计算长度
            if (!st[y]) dfs(y, ++len);
            else { //到这里是发现了环!
                ans = min(ans, len);
                return; //第一次发现的就是终点
            }
        }
    }
    
    int main() {
        cin >> n;
        for (int u = 1; u <= n; u++) {
            int x;
            cin >> x;
            add(u, x);
        }
        //拓扑排序
        topsort();
    
        //到这里,st[i]=false的就应该是环中结点,对环的部分dfs算最小环即可
        for (int i = 1; i <= n; i++) if (!st[i]) dfs(i, 1);
        cout << ans << endl;
    }
    
    
  • 相关阅读:
    IOS 两种控制器的使用,纯代码UITabBarController 与 UINavigationController
    iOS UI控件总结(全)
    IOS 参数string 转成url
    CocoaPods 的使用与一些异常情况的处理
    创建自己的 FrameWork(含demo)-Xcode7环境
    UITextView 一些属性的设置
    跳转第二弹
    iOS--登录注册页面-趣享-接口设计
    《大道至简》第一章阅读笔记
    软件工程个人作业02
  • 原文地址:https://www.cnblogs.com/littlehb/p/15126754.html
Copyright © 2011-2022 走看看