zoukankan      html  css  js  c++  java
  • 可达性统计

    给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。

    输入格式

    第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边。

    输出格式

    输出共N行,表示每个点能够到达的点的数量。

    数据范围

    1≤N,M≤30000<?XML:NAMESPACE PREFIX = "[default] http://www.w3.org/1998/Math/MathML" NS = "http://www.w3.org/1998/Math/MathML" />1≤N,M≤30000

    输入样例:
    10 10
    3 8
    2 3
    2 5
    5 9
    5 9
    2 3
    3 9
    4 8
    2 10
    4 9
    输出样例:
    1
    6
    3
    3
    2
    1
    1
    1
    1
    1


    首先,要求的是每个点出发能到达的点数包括它本身。注意是有向无环图。
    思路:
    1. 由推导可知(一看就知道嘿嘿),假设当前点是 x, 则通过x能到达的点,是由x
    所指向的点的指向的点的…..,的集合。
    2. 设f(x)为x点能到达的点数,
        f(x) = {x} U {f(son1), f(son2), …};//这里的son代表x指向的点,f(son)就是儿子能到
    达的点数。
    3.通过2的公式可以得知,计算应该从尾巴节点开始算,然后一步步累加, 所以我们可以先求出无向图的
    拓扑排序,然后反向的计算,因为拓扑排序反过来就是从尾巴开始的。
    4。看数据是30000,所以直接开二维数组会爆,这个时候就要用到二进制状态压缩,STL库中有bitset
    直接用就ok

    代码:
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <bitset>
    #include <queue>
    using namespace std;
    
    const  int N = 30010;
    int n, m;
    int h[N], e[N], ne[N], idx;
    
    int d[N], seq[N];//seq存拓扑排序
    
    bitset<N> f[N];
    
    void add(int a, int b) {//邻接表存图
        e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }
    
    void topsort() {
        queue<int> q;
        for(int i = 1; i <= n; ++ i){
            if(!d[i]) q.push(i);//入度为零的点加入队列
        }
        int k = 0;
        while(!q.empty()) {
            int t = q.front();
            q.pop();
            seq[k++] = t;
            for(int i = h[t]; ~i; i = ne[i])
            {
                int j = e[i];
                if(-- d[j] == 0)//入度为零的点加入队列
                    q.push(j);
            }
        }
    }
    
    int main() {
        cin >> n >> m;
        memset(h, -1, sizeof h);
        for(int i = 0; i < m; ++ i) {
            int a, b;
            cin >> a >> b;
            add(a,b);
            d[b] ++;//计算入度
    
        }
        topsort();
    
        for(int i = n - 1; ~i; -- i){
            int j = seq[i];//j点指向的下一个点
            f[j][j] = 1;//到它自己也算一个点
            for(int p = h[j]; ~p; p = ne[p])
                f[j] |= f[e[p]];//将对应的二进制位变成1
        }
        for(int i = 1; i <= n; ++ i) cout << f[i].count() << endl;//统计每个点的二进制位1的个数就是答案
        return 0;
    }




    追求吾之所爱
  • 相关阅读:
    《剑指Offer》算法题——“旋转数组”的最小数字
    驱动是如何运行的
    Logistic回归,梯度上升算法理论详解和实现
    Python 字符串前面加'r'
    Python中文编码问题(字符串前面加'u')
    最大长度回文子串(Manacher's algorithm)
    没有重复字符的子串的最大长度
    Python格式化输出
    python strip()函数和Split函数的用法总结
    Python中的sorted函数以及operator.itemgetter函数
  • 原文地址:https://www.cnblogs.com/rstz/p/14390996.html
Copyright © 2011-2022 走看看